Pull to refresh
11
0
Сергей @SergX

User

Send message
Не такое красивое решение как у Mrrl, но в те же 20 ходов и тоже в уме
Все повороты строго на Пи:

A1A3 B1B3 C1C3 A1A3 B1B3 A1A3 A2C2 A1C1 A3C3 A2C2
Можно, конечно, вынести static_cast в реализацию move_if_rr (как в представленном автором решении):
template<typename T, typename U> struct helper
    { typedef U&& result; };
template<typename T, typename U> struct helper<T&, U>
    { typedef U& result; };
template<typename T, typename U> struct helper<T const, U>
     : helper<T&, U> {};

template<typename T,typename U> typename helper<T,U>::result move_if_rr(U&u)
{
    return static_cast<helper<T,U>::result>( u );
}
С учетом представленного просто добавляем строчку
template<typename T, typename U> struct helper<T const, U> : helper<T&, U> {};


Весь код:
template<typename T, typename U> struct helper
{
    typedef U&& result;
    static U&& get( U& u ) { return static_cast<U&&>( u ); }
};

template<typename T, typename U> struct helper<T&, U>
{
    typedef U& result;
    static U& get( U& u ) { return u;}
};

template<typename T, typename U> struct helper<T const, U> : helper<T&, U> {};

template<typename T, typename U> typename helper<T,U>::result move_if_rr( U& u )
{
    return helper<T,U>::get( u );
}

Тест пройден.

Много написанного, но хотелось довести данный вариант до конца. Просто не всегда хочется иметь зависимости от дополнительных библиотек.
> а вот с константностью беда
Не учел.

Убираем const из специализации шаблона helper. Получаем:
template<typename T, typename U> struct helper
{
    typedef U&& result;
    static U&& get( U& u ) { return static_cast<U&&>( u ); }
};

template<typename T, typename U> struct helper<T&, U>
{
    typedef U& result;
    static U& get( U& u ) { return u;}
};

template<typename T, typename U> typename helper<T,U>::result move_if_rr( U& u )
{
    return helper<T,U>::get( u );
}


Комментируем или правим четвертый блок static_assert-ов в представленном тесте.
Тест проходит.

Что касается четвертого блока assert-ов (для /**/const A /**/).
Непонятно почему все возвращаемые типы имеют &, а не &&?
> Будет работать неправильно в ряде случаев:
Это не относится к реализации move_if_rr.

Уже не один раз в этой теме писали, что конструкция
 template< class T >  Container( T&& );

не работает в качестве конструктора копирования.
Можно сократить:
template<typename T, typename U> struct helper
{
    typedef U&& result;

    static U&& get( U& u )
    {
        return static_cast<U&&>( u );
    }
};

template<typename T, typename U> struct helper<T&, U>
{
    typedef U const& result;

    static U const& get( U const& u )
    {
        return u;
    }
};

template<typename T, typename U>
typename helper<T,U>::result move_if_rr( U& u )
{
    return helper<T,U>::get( u );
}
> успех засчитывается за функцию, которая работает правильно при условии, что Вы ее ни разу не отлаживали

Не учел… Несколько раз компилировал (проверить синтаксические ошибки)…
Если быть более точным, то не не определяет, а не объявляет.
Пока получился вот такой вариант:
template<typename T> struct helper
{
	template<typename U> struct result
	{
		typedef U&& type;
	};

	template<typename U> static U&& get( U& u )
	{
		return static_cast<U&&>(u);
	}
};

template<typename T> struct helper<T&>
{
	template<typename U> struct result
	{
		typedef U const& type;
	};

	template<typename U> static U const& get( U const& u )
	{
		return u;
	}
};

template<typename T, typename U>
typename helper<T>::template result<U>::type move_if_rr( U& v )
{
	return helper<T>::get( v );
}
Хотите Вы этого или нет, но в C++ для Вашего случая придется объявлять два конструктора:
some_class( some_class const& );
some_class( some_class&& );


Вариант
template<typename T> some_class( T&& );

не определяет конструктор копирования.
>А в чем проблема в публичный интерфейс включить нужные инклюды?
>Интерфейс на то и интерфейс, чтобы определять всё необходимое для работы.
Так они и так там включаются…

Если рассматривать порядок включения заголовочных файлов в файлах реализации (cpp), то, действительно, Ваш подход может показать несамодостаточность заголовочных файлов. Но не всегда, и, особенно, в тех случаях, когда включений немало. Некоторые примеры Вы привели сами.

Самый верный способ проверить самостоятелен ли заголовочный файл — это подключить его одного в отдельном файле cpp, и, конечно, не подключать в него больше ничего.

Встречный вопрос, как Ваш подход применим при реализации библиотеки разработчиком (при поставке ее только в виде заголовочных файлов)?

>Самодостаточность файлов это включает в себя независимый порядок их включения, разве не так?
Конечно, так. Статья, как раз, и нацелена показать, что сделать самодостаточными каждый заголовочный файл с учетом всех зависимостей — «нудная» задача и ее можно решить по-другому.

(Кстати, прежде чем нажать кнопку «Написать», можно выбрать кнопку «Предпросмотр» и увидеть как будет выглядеть комментарий)
В данном порядке Ваш вариант не подходит, поскольку открытый (публичный) интерфейс очень часто зависит от сторонних библиотек (STL, boost и др.).

Но если его обратить, то вполне:
а) #include STL;
б) #include 3rd-party библиотек (boost, Qt, libpng и т.д.);
в) внутренние #include библиотеки SomeLib;
г) публичные #include библиотеки SomeLib.

Единственное исключение — это то, что варианты в) и г) могут перемешиваться. Бывает так, что детали реализации завязаны на открытый интерфейс. (еще раз повторюсь мы рассматриваем библиотеки в варианте заголовочных файлов).

Если реализацию методов классов выносить в отдельные файлы, то их, конечно, нужно подключать самыми последними.
Не скажу за всех, но лично в моем понятии модуль обеспечивает:

1) Явное разделение интерфейса и реализации.
2) Возможность импорта имен из другого модуля и создания псевдонимов импортируемых элементов.
3) Обособленность модулей. Т.е. изменения в одном модуле, не затрагивающие его интерфейса, не влияют на определения в других модулях.
4) Так называемые конструкторы/деструкторы модулей и, как следствие, определенный порядок их вызова.

По пункту 1.
в С++ имеется возможность разбивать исходные коды на заголовочные файлы (*.h, *.hpp) и файлы реализации (*.cpp). Также в этом помогают пространства имен и идиома PImpl, за использование которой приходится платить динамическим созданием, косвенным доступом к членам класса impl и дополнительным кодом.

По пункту 2.
В C++ нельзя задать псевдонимы для функций и для шаблонных классов (в С++11, наконец-то, появились alias-ы для шаблонных классов). А введение элементов из других пространств имен в заголовочных файлах, в общем случае, не приветствуется.

Пункт 3.
Если файлы реализации можно обрабатывать независимо друг от друга, то заголовочные файлы нельзя. Поэтому, если файл реализации зависит от нескольких «модулей», то заголовочные файлы каждого модуля компилируются вместе. Как следствие, возможны сюрпризы с порядком включения, #define-ами, специализациями шаблонов и другое.

Пункт 4.
По стандарту C++ порядок инициализации глобальных объектов не определен. Есть обходные пути, но не на все случаи.

Дополнительно хочется отметить, что введение модулей, по идее, должно значительно уменьшить время компиляции. Сейчас в C++ одно и то же компилируется по нескольку раз в каждой единице трансляции (прекомпилируемые заголовки не всегда помогают).
Спасибо, поправил.
#pragma once выглядит немного лучше, чем #ifndef #define #endif. Но в целом, согласен.
Только лучше не просто import, а полноценные модули в C++.
Проблема только в том, что это не описывается стандартом.

А по существу, и GCC и MSVC (и даже VisualDSP++) поддерживает данное расширение.
Автор вопроса при описании отличий своего варианта от решения 2 топика написал:
«что все оборачивается под одним неймспейсом без разделения (namespace my_lib пишется только один раз)».

Я возразил. Может быть, мысль была неудачно сформулирована.
Не туда написал ответ. Смотрите пост ниже.

И еще. Предлагаемый способ лишен одного недостатка (хотя для кого как), присущего всем остальным и, в частности, Вашему варианту. А именно: явное указание пространств имен при использовании классов других библиотек.

Помимо это, использование идиомы PImpl в стандартном для него виде, выливается в значительное количество кода, необходимого разработчику библиотеки для написания каждого такого класса.
Я прошу Вас, будьте внимательны. Автор вопроса указал разницу между предлагаемым им вариантом и решением 2, представленным в топике. Я этой разницы не заметил, указав на то место в описании варианта, при котором оба варианта становятся эквивалентными.
Сам написал не одну библиотеку (и не маленькие). Идеи написания шли рядом со стандартными, особенно с теми, что применялись в boost-e. Устал от сложности языка, а точнее, от тех шаблонов, к которым мы привыкли.

Перечитайте, пожалуйста, топик. В нем ясно сказано, что организация файлов в нем не рассматривается. Это будет сделано позже — в следующих топиках. Подождите, немного: сами все увидите…

По поводу PImpl. Заставлять пользователей всегда использовать динамическое выделение объекта класса реализации, это, уж извините, не очень хорошо. Пусть разработчик библиотеки решает, что ему лучше…
1

Information

Rating
Does not participate
Location
Орел, Орловская обл., Россия
Date of birth
Registered
Activity