2015-06-18 3 views
6

Это, похоже, другое: «Кто делает это хорошо?» вопрос, так как gcc 6.0.0 и clang 3.7.0 ведут себя иначе.Перенаправление аргумента, отличного от типа, вызывает различное поведение в шаблоне переменной

Давайте предположим, что мы имеем переменную шаблон, который принимает const char * как не аргумент шаблона и специализирован для данного указателя:

constexpr char INSTANCE_NAME[]{"FOO"}; 

struct Struct{ void function() const { std::cout << __PRETTY_FUNCTION__; } }; 
std::ostream &operator <<(std::ostream &o, const Struct &) { return o << INSTANCE_NAME; } 

template <const char *> char Value[]{"UNKNOWN"}; 
// spezialization when the pointer is INSTANCE_NAME 
template <   > Struct Value<INSTANCE_NAME>{}; 

Обратите внимание, что переменная шаблона имеют различные типы в зависимости от специализации. Десять у нас есть две функции шаблона, каждый из одного принимает const char * как, не аргумент шаблона и вперед его переменного шаблон:

template <const char *NAME> void print() 
{ 
    std::cout << Value<NAME> << '\n'; 
} 

template <const char *NAME> void call_function() 
{ 
    Value<NAME>.function(); 
} 

Тогда вызов этого функция результаты в разных режимах:

int main() 
{ 
    print<INSTANCE_NAME>(); 
    call_function<INSTANCE_NAME>(); 

    return 0; 
} 

Code Here

лязг 3.7.0 печатает FOO и void Struct::function() const (Как я ожидал) в то время как GCC 6.0.0 не может скомпилировать с ошибкой ниже:

запрос члена «функции» в «Value», который имеет тип неклассовую «полукокса [8]»

Я почти уверен, что НКУ не удались вперед в шаблоне, не аргумент типа NAME переменного шаблона Value в функции call_function и по этой причине он выбирает неразделенный переменный шаблон, который является один с 'char [8]' типом ...

Он действует так, как будто он копирует аргумент шаблона. Это происходит только при вызове функции-члена объекта, если мы прокомментируем тело call_function, выход FOO не UNKNOWN, поэтому в функции printпересылка работает даже в gcc.

Так

  • Что правильное поведение? (mi bet for clang)
  • Как я могу открыть биг-код для компилятора, который делает это неправильно?
+0

@ BЈовић вы можете, если 'const char *' имеют внешнюю связь ([см. Этот ответ] (http://stackoverflow.com/a/16402606/499359)). При внешней связи он всегда будет иметь тот же адрес; думайте об этом, как будто это «int». –

+2

Просто примечание: наличие совершенно разных конструкций под названием 'FOO',' Foo' и 'foo' затрудняет мысленный анализ вашего примера. 'MyCharP',' MyStruct' и 'myFun' или подобное было бы проще. – TartanLlama

+0

@TartanLlama Я изменил имена, спасибо за предложение :) –

ответ

3

Существует разумный консенсус, что переменные специализации шаблона разрешаются изменять тип переменного шаблона: C++1y/C++14: Variable Template Specialization?

поведения GCC особенно интересно, если типа по умолчанию Value изменяется к типу с function метод:

struct Unknown{ void function() const { std::cout << __PRETTY_FUNCTION__; } }; 
template <const char *> Unknown Value; 

prog.cc: In instantiation of 'void call_function() [with const char* NAME = ((const char*)(& INSTANCE_NAME))]': 
prog.cc:26:18: required from here 
prog.cc:20:5: error: 'Unknown::function() const' is not a member of 'Struct' 
    Value<NAME>.function(); 
    ^

Исправлена ​​ошибка, по-видимому, что там, где неспециализированная переменная шаблона имеет тип, который не зависит от параметров переменного шаблона шаблона, GCC предполагает в шаблонных методах, которые используют этот шаблон переменной, что шаблон переменной всегда имеет этот тип.

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

Другим (возможно, более простым) обходным путем является создание неспециализированного типа шаблона переменной, как-то зависящего от параметров шаблона шаблона переменной; в вашем случае это будет работать:

template <const char *P> decltype(*P) Value[]{"UNKNOWN"}; 

Я не могу найти соответствующий вопрос в gcc bugzilla так что вы можете ввести новый. Вот минимальный пример:

struct U { void f() {} }; 
struct V { void f() {} }; 
template<class T> U t; 
template<> V t<int>; 
template<class T> void g() { t<T>.f(); } 
int main() { g<int>(); } 
+0

Я добавил проблему (надеюсь, что я сделал это хорошо, это мой первый отчет об ошибке) Я использовал ваш пример и ваш текст (потому что английский не мой первый язык, и я не уверен, писал ли я что-то странное), большое спасибо. –

+0

@PaperBirdMaster выглядит хорошо; Я добавил вывод ошибки. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66596 – ecatmur

5

Интересно, что GCC в этом примере даже противоречит друг другу.

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

template <typename T> 
struct type_check; 

Мы также сделать еще const char*, что мы можем использовать для тестирования:

constexpr char NOT_FOO[]{"NOT_FOO"}; 

Теперь мы видим, что компилятор давится:

template <const char *NAME> void foo() 
{ 
    type_check<decltype(Value<FOO>)> a; 
    type_check<decltype(Value<NAME>)> b; 
    type_check<decltype(Value<NOT_FOO>)> c; 
    type_check<decltype(Value<FOO>.foo())> d; 
    type_check<decltype(Value<NAME>.foo())> e; 
    type_check<decltype(Value<NOT_FOO>.foo())> f; 
} 

H ЭРД ошибки, которые производят GCC 5.1.0 (отредактированную немного для ясности):

test.cpp:21:38: error: ‘type_check<Foo> a’ has incomplete type 
    type_check<decltype(Value<FOO>)> a; 
            ^
test.cpp:22:39: error: ‘type_check<Foo> b’ has incomplete type 
    type_check<decltype(Value<NAME>)> b; 

test.cpp:25:42: error: ‘type_check<char [8]> c’ has incomplete type 
    type_check<decltype(Value<NOT_FOO>)> c; 
            ^
test.cpp:23:44: error: ‘type_check<void> c’ has incomplete type 
    type_check<decltype(Value<FOO>.foo())> c; 

test.cpp:24:37: error: request for member ‘foo’ in ‘Value<NAME>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NAME>.foo())> d; 

test.cpp:28:40: error: request for member ‘foo’ in ‘Value<((const char*)(& NOT_FOO))>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NOT_FOO>.foo())> f; 

Давайте рассмотрим каждый из них в то время.


Ошибка 1:

test.cpp:21:38: error: ‘type_check<Foo> a’ has incomplete type 
    type_check<decltype(Value<FOO>)> a; 

В первой ошибки, мы можем видеть, что GCC правильно делает вывод, что тип Value<FOO> является Foo. Это то, чего мы ожидаем.

Ошибка 2:

test.cpp:22:39: error: ‘type_check<Foo> b’ has incomplete type 
    type_check<decltype(Value<NAME>)> b; 

Здесь, GCC правильно делает переадресацию и работает, что Value<NAME> имеет тип Foo.

Ошибка 3:

test.cpp:25:42: error: ‘type_check<char [8]> c’ has incomplete type 
    type_check<decltype(Value<NOT_FOO>)> c; 

Отлично Value<NOT_FOO> является "UNKNOWN", так что это правильно.

Ошибка 4:

test.cpp:23:44: error: ‘type_check<void> c’ has incomplete type 
    type_check<decltype(Value<FOO>.foo())> c; 

Это нормально, Value<FOO> является Foo, которую мы можем назвать foo на, возвращая void.

Ошибка 5:

test.cpp:24:37: error: request for member ‘foo’ in ‘Value<NAME>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NAME>.foo())> d; 

Это нечетный. Хотя при ошибке 2 мы видим, что GCC знает, что тип Value<NAME> равен Foo, когда он пытается выполнить поиск функции foo, он ошибается и вместо этого использует основной шаблон. Это может быть некоторая ошибка в поиске функции, которая неправильно решает значения аргументов шаблона непигового типа.

Ошибка 6:

test.cpp:28:40: error: request for member ‘foo’ in ‘Value<((const char*)(& NOT_FOO))>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NOT_FOO>.foo())> f; 

Здесь мы можем увидеть компилятор правильно выбрать исходный шаблон при работе, что Value<NOT_FOO> есть. То, что меня интересует, это (const char*)(& NOT_FOO)), который GCC выводит как тип NOT_FOO. Может быть, это указатель на эту проблему? Я не уверен.

Я предлагаю подавать ошибку и указывать на несоответствие. Возможно, это не полностью отвечает на ваш вопрос, но я надеюсь, что это поможет.