Это действительно зависимое имя шаблона? - PullRequest
2 голосов
/ 18 марта 2020

У меня есть код, который не компилируется, и я уменьшил его до следующей минимальной версии:

class Builder
{
    public:
        Builder()
        {}

        auto foo(int) -> Builder &
        {
            return *this;
        }

        template<typename T>
        auto bar() -> Builder &
        {
            return *this;
        }
};

template<typename T>
Builder get_builder()
{
    return Builder().foo(T()).bar<T>();
}

int main()
{
    auto builder = get_builder<int>();
    (void) builder;
}

См. В Wandbox

Clang (9.0.0 ) отклоняет это следующим образом:

prog.cc:22:31: error: use 'template' keyword to treat 'bar' as a dependent template name
    return Builder().foo(T()).bar<T>();
                              ^
                              template 

Прав ли Кланг, говоря, что bar является зависимым именем шаблона? VS 2017 не видит проблем. G CC (9.2.0) также отклоняет код, но с гораздо более неясным сообщением об ошибке:

prog.cc: In function 'Builder get_builder()':
prog.cc:22:36: error: expected primary-expression before '>' token
   22 |     return Builder().foo(T()).bar<T>();
      |                                    ^
prog.cc:22:38: error: expected primary-expression before ')' token
   22 |     return Builder().foo(T()).bar<T>();
      |       

Изменение ошибочной строки, как предлагает Clang

return Builder().foo(T()).template bar<T>();

исправляет компиляцию для Clang и G CC. VS2017 также принимает эту версию.

Кажется, решение простое , но если я переупорядочу вызовы функций:

return Builder().bar<T>().foo(T());

или удалим параметр из foo :

return Builder().foo().bar<T>();

ошибка исчезла.

Что здесь происходит?

  1. Правильно ли Кланг и G CC отвергнуть оригинальную версию?
  2. Правильно ли Clang и G CC принимают измененные версии (переупорядочены, изменены foo)? Если так, то почему? В чем разница?

1 Ответ

1 голос
/ 18 марта 2020

По моему опыту, MSV C (неправильно) допускает код, для которого требуется ключевое слово template (даже с определением /permissive-).

Код, о котором идет речь , не требует template для устранения неоднозначности того, что bar является именем шаблона. g cc и clang верны в диагностике этого. Возможно, присутствует перегрузка foo, которая возвращает другой тип в зависимости от T. Поэтому вызов bar зависит от параметра шаблона.

Рассмотрим следующее:

struct FooBar
{
   template<class T>
   auto bar() -> T
   {
       return 42;
   }
};

class Builder
{
    public:
        Builder()
        {}

        auto foo(int) -> Builder &
        {
            return *this;
        }

        auto foo(short) -> FooBar
        {
            return {};
        }

        template<typename T>
        auto bar() -> Builder &
        {
            return *this;
        }
};

, вызываемый с помощью:

auto builder = get_builder<short>();

Возможность перегрузки (не фактическое присутствие) делает имя зависимым. См. [temp.dep] ( выделение шахта):

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

Компилятор не обязан проверять все экземпляры шаблона на предмет наличия только одной возможной функции в наборе перегрузки.

...