Использование шаблонов псевдонимов для sfinae: язык позволяет это? - PullRequest
0 голосов
/ 28 августа 2018

Я только что открыл следующую технику. Он выглядит очень близко к одному из предложенных концепций синтаксиса, отлично работает на Clang, GCC и MSVC.

template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;

template <typename T>
void foo(require_rvalue<T> val);

Я пытался найти его с помощью поисковых запросов типа "sfinae in type alias" и ничего не получил. Есть ли название для этой техники, и язык действительно позволяет это?


Полный пример:

#include <type_traits>

template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;

template <typename T>
void foo(require_rvalue<T>)
{
}

int main()
{
    int i = 0;
    const int ic = 0;
    foo(i);            // fail to compile, as desired
    foo(ic);           // fail to compile, as desired
    foo(std::move(i)); // ok
    foo(123);          // ok
}

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

Это работает и разрешено, потому что оно опирается на широко используемые функции C ++, разрешенные стандартом:

  1. SFINAE в параметрах функции ( [temp.over] / 1 , [temp.deduct] / 6 , [temp.deduct] / 8 ):

    template <typename T>
    void foo(T&& v, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr)
    { /* ... */ }
    

    мы не можем вывести фактический параметр, такой как void foo(typename std::enable_if<std::is_rvalue_reference<T&&>::value, T>::type&&) ( CWG # 549 ), но можно обойти это ограничение с помощью псевдонимов шаблона (это уловка, которую я имею представлены в моем вопросе)

  2. SFINAE в объявлении параметра шаблона ( [temp.deduct] / 7 ):

    template <typename T, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr>
    void foo(T&& v)
    { /* ... */ }
    
  3. Шаблоны псевдонимов в параметрах функций ( [temp.alias] / 2 ):

    template<class T> struct Alloc { /* ... */ };
    template<class T> using Vec = vector<T, Alloc<T>>;
    
    template<class T>
      void process(Vec<T>& v)
      { /* ... */ }
    
  4. Шаблоны псевдонимов могут иметь параметры по умолчанию ( [temp.param] / 12 , [temp.param] / 15 , [temp.param] / 18 )

  5. Параметры шаблонов шаблонов псевдонимов, параметризованных с помощью выводимых типов, все еще могут быть выведены ( [temp.deduct.type] / 17 ):

Я принял ответ @ Барри и вставил его (со сконцентрированной информацией и обо всех аспектах, которые использует трюк), потому что многие люди (включая меня) боятся стандартного языка вуду С ++ о вещах вывода шаблонов.

0 голосов
/ 28 августа 2018

[...] язык фактически позволяет это?

Не могу ничего сказать об имени, но мне кажется, что да.

Соответствующая формулировка: [temp.alias] / 2 :

Когда template-id относится к специализации шаблона псевдонима, он эквивалентен связанному типу, полученному путем замены его template-arguments на шаблон -параметры в идентификатор типа шаблона псевдонима.

и правило sfinae, [temp.deduct] / 8 :

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

Взятие аргумента типа require_rvalue<T> ведет себя так, как будто мы подставляем этот псевдоним, который либо дает нам T&&, либо ошибку замещения - и эта ошибка замещения, вероятно, находится в непосредственном контексте & dagger; замены и, таким образом, является "дружественным к сфине", а не серьезной ошибкой. Обратите внимание, что хотя аргумент по умолчанию не используется, в результате CWG 1558 (правило void_t) мы получили добавление [temp.alias] / 3 :

Однако, если template-id является зависимым, последующая замена аргумента шаблона по-прежнему применяется к template-id .

Это гарантирует, что мы по-прежнему подставляем аргумент по умолчанию для запуска требуемого сбоя замещения.

Вторая недосказанная часть вопроса заключается в том, может ли это фактически вести себя как ссылка для пересылки. Правило есть в [temp.deduct.call] / 3 :

A ссылка на пересылку - это rvalue-ссылка на неквалифицированный cv параметр шаблона, который не представляет параметр шаблона шаблона класса (во время выведения аргумента шаблона класса ([over.match.class.deduct] )). Если P является ссылкой для пересылки, а аргумент является lvalue, тип «lvalue ссылка на A» используется вместо A для вывода типа.

Является ли шаблон псевдонима с одним параметром шаблона, связанный тип которого является rvalue-ссылкой на его cv-неквалифицированный параметр шаблона, ссылкой для пересылки? Ну, [temp.alias] / 2 говорит, что require_rvalue<T> эквивалентно T&&, а T&& - правильная вещь. Так что возможно ... да.

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

<ч /> & dagger; Хотя, обратите внимание на существование CWG 1844 и отсутствие фактического определения для непосредственного контекста, и пример там, который также основан на сбое замещения из аргумента по умолчанию - который Проблемы государств имеют расхождение в реализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...