2016-11-20 5 views
7

Похоже, что правила для экземпляра шаблона в Clang (3.8) и GNU C++ (4.9) не совпадают. Ниже приведен пример:Создание экземпляра шаблона в GNU C++ и Clang

#include <cstddef> 

template <bool> 
class Assert { 
    Assert();   // private constructor for Assert<false> 
}; 

template <> 
class Assert<true> { // implicit public constructor for Assert<true> 
}; 

template <size_t N> 
class A { 
}; 

template <class T, size_t N> 
T foo(A<N>) { 
    return T(N - 1); 
} 

template <class T> 
T foo(A<0>) {  // foo is not defined for N=0 
    Assert<false>(); 
    return T(0); 
} 

int main(int argc, char **argv) { 
    foo<int>(A<3>()); 
    return 0; 
} 

Этот минимальный пример показывает функцию шаблона, foo, что обобщенно над типа T и натурального числа N. Эта функция не определена для N=0, поэтому я хотел бы использовать класс Assert, чтобы сигнализировать ошибку компилятора, если он используется таким образом.

Этот код принят компилятором GNU (и Visual C++ 2015, а также), но Clang дает сообщение об ошибке «вызов частного конструктора класса Assert<false>».

Итак, кто прав? Как я вижу, нет вызова для foo<T,0>, поэтому нет необходимости создавать экземпляр этого шаблона ...

EDIT: Принимая интерпретацию стандарта Clang стандартом, каков канонический способ принудительной проверки времени компиляции шаблона параметры?

ответ

5

Я считаю, что clang верен, поскольку Assert<false> не является зависимым.

http://en.cppreference.com/w/cpp/language/dependent_name

Non-зависимые имена ищутся и связаны в точке определения шаблона. Это связывание выполняется, даже если в момент создания шаблона есть лучшее совпадение:

Не делайте специализацию, которая не может быть действительной. Сделайте их общедоступными и используйте static_assert (с зависимым значением), чтобы проверить недопустимые типы/значения аргументов шаблона. static_assert(std::is_same<T, int>::value) или static_assert(N != 0)

+0

Так что 'Assert ' проверяется и проверяется, даже если 'foo ' никогда не был создан? Разве это не лишняя работа? В любом случае, учитывая, что он работает так, что является стандартным способом утверждения компилятора в параметре шаблона? (Я отредактировал вопрос соответствующим образом.) – vukung

+0

@vukung На самом деле это меньше работает, потому что компилятор должен искать 'Assert ' один раз, когда шаблон определен, а не каждый раз, когда создается экземпляр шаблона. – Oktalist

+0

Зачем вам писать код, который никогда не будет использоваться? Это звучит как много дополнительной работы для ВАС. – xaxxon

5

Принимая интерпретацию звона по стандарту, что канонический способ обеспечить проверку во время компиляции на параметрах шаблона?

Вы можете отказаться от «специализации»/перегрузка foo() для A<0> и определяют общий шаблон следующим образом:

template <class T, size_t N> 
T foo(A<N>) { 
    Assert<N != 0>(); 
    return T(N - 1); 
} 

С C++ 11 вы не должны определить свой собственный статический Assert и может использовать язык при условии static_assert:

template <class T, size_t N> 
T foo(A<N>) { 
    static_assert(N!=0, "N must be positive"); 
    return T(N - 1); 
} 
5

Оба компиляторы являются правильными. Как обычно, это контролируется [temp.res]/8:

Зная, какие имена являются именами типов позволяет синтаксис каждого шаблона для проверки. Программа не плохо формируется, никаких диагностических требуется, если:

  • не действует специализация может быть сгенерирован для шаблона или подоператора из constexpr if в заявлении ([зЬтЬ.если]) в шаблон и шаблон не создается экземпляр, или

  • каждый действует специализация VARIADIC шаблона требует пустого параметра шаблона пакета, или

  • гипотетический конкретизацией шаблона сразу после его определения будет плохо сформирован из-за конструкции, которая не зависит от параметра шаблона, или

  • интерпретации такой конструкции в гипотетическом экземпляре отличается от интерпретации соответствующей конструкция в любом переменном токе tual экземпляра шаблона.

Шаблон идет вразрез с третьей точки пули.

Как к правильному решению, либо подходящий static_assert можно использовать, или вы можете явно удалить нежелательные перегрузки:

template <class T> 
T foo(A<0>) = delete; 

Первого позволяет лучше сообщения об ошибках, последний играет более красиво с другим метапрограммированием.

Смежные вопросы