decltype ((void) T {}) в шаблоне частичной специализации не выводить? - PullRequest
0 голосов
/ 30 июня 2018
template<typename T,typename U = void>
struct Test
{
    static const int value = 0;
};
template<typename T>
struct Test<T, decltype((void)T{})>
{
    static const int value = 2;
};

template<typename T>
struct Test<T*,  decltype((void)T{})>
{
    static const int value = 1;
};

int main(){       
   cout<<Test<int*>::value<<endl;
    return 0;
}

код в gcc / clang оба получают ошибку: неоднозначно, но изменение decltype на void_t нормально. Почему?

1 Ответ

0 голосов
/ 05 июля 2018

Для меня это выглядит как ошибка компилятора: вам действительно нужны побочные эффекты от T{}? Вся конструкция decltype((void)T{}) должна отбрасывать инициализированный нулями T, что позволяет получить неоцененные побочные эффекты - так какой смысл в таком побочном эффекте, если он есть?
Если вы упростите его, просто используя void или даже пропустив весь второй тип, проблема сразу исчезнет ( живая демонстрация на wandbox ):

template<typename T, typename U = void>
struct Test
{
    static const int value = 0;
};
template<typename T>
struct Test<T, void>
{
    static const int value = 2;
};

template<typename T>
struct Test<T*, void>
{
    static const int value = 1;
};

int main(){       
    std::cout << Test<int*>::value << "\n";
    return 0;
}

Может быть, есть некоторые сложные детали, которые я пропустил, но я пробовал много вариантов, включая комбинации std::declval, std::result_of, шаблоны псевдонимов, typedefs, ни одна из которых не дала мне ничего значимого.
Но если мы переместим часть decltype((void)T{}):

template<typename T>
using wrap = decltype((void)T{});

template<typename T>
struct Test<T, wrap<T>>
{
    static const int value = 2;
};
template<typename T>
struct Test<T*, wrap<T>>
{
    static const int value = 1;
};

GCC кричит: ambiguous template instantiation for 'struct Test<int*, void>'. Что для меня является признаком того, что что-то идет не в том направлении.

...