decltype (auto) в некоторых случаях работает со SFINAE? - PullRequest
0 голосов
/ 01 июля 2018

Я предполагал, что decltype(auto) является несовместимой конструкцией, когда используется для попытки и SFINAE от возвращаемого типа. Таким образом, если вы получили бы ошибку замещения, вы получите серьезную ошибку

Но почему работает следующая программа? https://wandbox.org/permlink/xyvxYsakTD1tM3yl

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

template <typename T>
class Identity {
public:
  using type = T;
};

template <typename T>
decltype(auto) construct(T&&) {
  return T{};
}

template <typename T, typename = std::void_t<>>
class Foo {
public:
  static void foo() {
    cout << "Nonspecialized foo called" << endl;
  }
};
template <typename T>
class Foo<T,
          std::void_t<typename decltype(construct(T{}))::type>> {
public:
  static void foo() {
    cout << "Specialized foo called" << endl;
  }
};

int main() {
  Foo<Identity<int>>::foo();
  Foo<int>::foo();
}

Разве мы не должны получить серьезную ошибку, когда Foo создается с int? Учитывая, что int не имеет псевдонима члена с именем type?

1 Ответ

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

Я предполагал, что decltype(auto) является несовместимой конструкцией, когда используется для попытки и SFINAE от возвращаемого типа.

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

Однако, в этом примере, единственный способ получить ошибку замещения в теле - это если бы T не было конструируемым по умолчанию. Но вы вызываете construct с помощью T{}, который уже требует, чтобы он был конструируемым по умолчанию - чтобы сбой произошел первым или никогда.

Вместо этого произошедшая ошибка замещения находится в непосредственном контексте подстановки в typename decltype(construct(T{}))::type. Попытка получить ::type от int происходит, когда мы находимся в непосредственном контексте создания аргументов шаблона для Foo, поэтому SFINAE все еще применяется.

Пример, демонстрирующий, где decltype(auto) нарушает SFINAE, если мы вместо этого реализовали это как:

template <typename T>
decltype(auto) construct() {
  return T{};
}

template <typename T, typename = std::void_t<>>
class Foo {
public:
  static void foo() {
    cout << "Nonspecialized foo called" << endl;
  }
};
template <typename T>
class Foo<T,
          std::void_t<typename decltype(construct<T>())::type>> {
public:
  static void foo() {
    cout << "Specialized foo called" << endl;
  }
};

А потом попытался создать экземпляр:

struct X {
    X(int);
};

Foo<X>::foo(); // hard error, failure is in the body of construct()
...