Является ли мой код правильным C++ 11?
Выглядит правильно. Кроме того, компилируется как с gcc, так и с clang с -Wall -Wextra
.
Является ли это ошибкой VC?
Скорее всего. VC является пресловутым в этом отношении, см., Например, What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation? или google msvc two-phase lookup.
Как я мог специализироваться на std :: begin и std :: end (и, следовательно, на основе диапазона для циклов) для неизменяемого старого класса контейнеров, если эта специализация не работает?
Для кода вы предоставили, обходной путь будет использовать ЬурейеЕ:
#include <iostream>
int myint() { return 1; }
typedef decltype(myint()) return_type;
template<class T>
return_type f()
{
std::cout << "general\n";
return 1;
}
template <>
return_type f<double>()
{
std::cout << "special\n";
return 2;
}
int main()
{
f<int>();
f<double>();
}
Все три основные компиляторы (НКУ, лязг, против), кажется, чтобы быть счастливым с этим кодом.
UPDATE:
Как я мог специализироваться std::begin
и std::end
(и, таким образом, обеспечивают диапазон на основе для петель) для неизменного унаследованного класса контейнера, если такого рода специализации не Работа?
[И по комментариям:] Я думал, что специализация std::begin
и std::end
всегда была лучшим подходом.
После придав ему некоторые мысли, специализируясь std::begin()
и std::end()
будет моим последним средством. Моя первая попытка состояла бы в том, чтобы предоставить функции begin()
и end()
; к сожалению, это не вариант для вас, потому что вы не можете изменить соответствующий код. Тогда, моя вторая попытка будет предоставлять бесплатные функции в моем собственном пространстве имен:
#include <iostream>
#include <initializer_list>
#include <vector>
namespace my_namespace {
template <typename T> class my_container;
template <typename T> T* begin(my_container<T>& c);
template <typename T> T* end(my_container<T>& c);
template <typename T>
class my_container {
public:
explicit my_container(std::initializer_list<T> list) : v(list) { }
friend T* begin<>(my_container& c);
friend T* end<>(my_container& c);
private:
std::vector<T> v;
};
template <typename T>
T* begin(my_container<T>& c) {
return c.v.data();
}
template <typename T>
T* end(my_container<T>& c) {
return c.v.data()+c.v.size();
}
}
int main() {
my_namespace::my_container<int> c{1, 2, 3};
for (int i : c)
std::cout << i << '\n';
}
Этот подход должен работать, если вы были в состоянии специализироваться std::begin()
и std::end()
для контейнера. Он также работает, если вы делаете это в глобальном пространстве имен (т. Е. Вы просто опускаете namespace my_namespace {
и закрываете }
), но я предпочел включить свою реализацию в свое собственное пространство имен.
Смотрите также
Спасибо за ЬурейеЕ трюк, который был определенно новым для меня! Я все еще пытаюсь понять, что действительно идиоматично в C++ 11 относительно std :: begin et al. Возможно, если моя главная проблема заключается в предоставлении циклов, основанных на диапазонах, мне все равно не нужно беспокоиться о std :: begin/std :: end и просто предоставлять автономные функции запуска/завершения без функции std? –
@ChristianHackl Свободностоящие 'std :: begin()' и 'std :: end()' удобны для старых массивов стилей C (например, 'int v [42];'). Если вы хотите, чтобы ваши старые контейнеры работали с новыми циклами на основе циклов, просто предоставьте функции 'my_container.begin()' и 'my_container.end()' * member *. Предполагая, что вы можете изменить старые контейнеры, но вы пишете unchangable ... Хм. Кроме того, вы можете определить представления для старых контейнеров, которые не требуют изменения старого кода. В любом случае, если достаточно простого typedef, я бы пошел с typedef. – Ali
Я думал о чем-то в том же духе ... LegacyContainerLoopAdapter wrapping LegacyContainer с функциями-членами begin()/end(). Но тогда, конечно, std :: begin/std :: end также представляется удобным в качестве основных операций для обобщенных более высоких уровней (например, что-то вроде bool empty (T const & c) {return std :: begin (c) = = std :: end (c);}). Вот почему я думал, что специализация std :: begin и std :: end всегда были лучшим подходом. –