Неоднозначные частичные специализации с Clang в C ++ 17 - PullRequest
0 голосов
/ 01 июля 2018
template <typename Foo, Foo Part>
struct TSelect {};

enum What {
    The
};

template <typename Foo>
struct AnotherOneSelector {
    static constexpr Foo Id = Foo::The;
};

template <typename Foo, typename SelectPartType>
struct THelper;

template <typename Foo>
struct THelper<Foo, TSelect<Foo, AnotherOneSelector<Foo>::Id>> {};

template <typename Foo, Foo PartId>
struct THelper<Foo, TSelect<Foo, PartId>> {};

int main() {
    THelper<What, TSelect<What, What::The>> t;
}

Этот код компилируется с gcc8.1 с каждой стандартной опцией (c ++ 11, c ++ 14, c ++ 17), но clang trunk не с c ++ 17 ( хотя с c ++ 14 все нормально).

Сообщение об ошибке:

test.cpp:23:49: error: ambiguous partial specializations of 'THelper<What, TSelect<What, The> >'
        THelper<What, TSelect<What, What::The>> t;
                                                ^
test.cpp:17:12: note: partial specialization matches [with Foo = What]
    struct THelper<Foo, TSelect<Foo, AnotherOneSelector<Foo>::Id>> {};
           ^
test.cpp:20:12: note: partial specialization matches [with Foo = What, PartId = The]
    struct THelper<Foo, TSelect<Foo, PartId>> {};
           ^
1 error generated.

Какой компилятор правильный? Я не видел никаких изменений в шаблоне специализация на С ++ 17.

1 Ответ

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

Разница в C ++ 17 заключается в том, что вы можете определить тип параметра не-типа из соответствующего аргумента. И Кланг явно делает неправильный вывод.

В данном случае вы должны синтезировать уникальный тип для Foo и попытаться вывести Foo и PartId в THelper<Foo, TSelect<Foo, PartId>> против THelper<Unique, TSelect<Unique, AnotherOneSelector<Unique>::Id>>. Кажется, что происходит то, что Clang обрабатывает AnotherOneSelector<Unique>::Id, чтобы иметь какой-то отдельный уникальный тип - назовите его Unique2 - так что вывод в С ++ 17 завершится неудачно, потому что вы вывели конфликтующие типы для Foo. Обработка таких невысказанных контекстов, как этот, общеизвестно занижена, но я вполне уверен, что предполагается выводить, используя тип преобразованного аргумента шаблона, а не исходный.

Два возможных обходных пути:

  • Подавить вывод Foo из аргумента, не относящегося к типу, помещая тип в невыгружаемый контекст. Например: template <typename Foo, std::remove_const_t<Foo> PartId>.
  • Принудительное преобразование в Foo в аргументе шаблона, чтобы избежать ложного конфликта: struct THelper<Foo, TSelect<Foo, Foo{AnotherOneSelector<Foo>::Id}>>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...