Параметр шаблона по умолчанию & лямбда в недооцененном контексте: ошибка или функция? - PullRequest
38 голосов
/ 02 апреля 2019

Мы рассматриваем цель создания двух разных типов, используя один и тот же синтаксис. Это легко сделать с помощью лямбды:

auto x = []{};
auto y = []{};
static_assert(!std::is_same_v<decltype(x), decltype(y)>);

Но вместо того, чтобы использовать лямбды, мы ищем другой, более элегантный синтаксис. Вот несколько тестов. Начнем с определения некоторых инструментов:

#include <iostream>
#include <type_traits>
#define macro object<decltype([]{})>
#define singleton object<decltype([]{})>

constexpr auto function() noexcept
{
    return []{};
}

template <class T = decltype([]{})>
constexpr auto defaulted(T arg = {}) noexcept
{
    return arg;
}

template <class T = decltype([]{})>
struct object
{
    constexpr object() noexcept {}
};

template <class T>
struct ctad
{
    template <class... Args>
    constexpr ctad(const Args&...) noexcept {}
};

template <class... Args>
ctad(const Args&...) -> ctad<decltype([]{})>;

и следующие переменные:

// Lambdas
constexpr auto x0 = []{};
constexpr auto y0 = []{};
constexpr bool ok0 = !std::is_same_v<decltype(x0), decltype(y0)>;

// Function
constexpr auto x1 = function();
constexpr auto y1 = function();
constexpr bool ok1 = !std::is_same_v<decltype(x1), decltype(y1)>;

// Defaulted
constexpr auto x2 = defaulted();
constexpr auto y2 = defaulted();
constexpr bool ok2 = !std::is_same_v<decltype(x2), decltype(y2)>;

// Object
constexpr auto x3 = object();
constexpr auto y3 = object();
constexpr bool ok3 = !std::is_same_v<decltype(x3), decltype(y3)>;

// Ctad
constexpr auto x4 = ctad();
constexpr auto y4 = ctad();
constexpr bool ok4 = !std::is_same_v<decltype(x4), decltype(y4)>;

// Macro
constexpr auto x5 = macro();
constexpr auto y5 = macro();
constexpr bool ok5 = !std::is_same_v<decltype(x5), decltype(y5)>;

// Singleton
constexpr singleton x6;
constexpr singleton y6;
constexpr bool ok6 = !std::is_same_v<decltype(x6), decltype(y6)>;

и следующий тест:

int main(int argc, char* argv[])
{
    // Assertions
    static_assert(ok0); // lambdas
    //static_assert(ok1); // function
    static_assert(ok2); // defaulted function
    static_assert(ok3); // defaulted class
    //static_assert(ok4); // CTAD
    static_assert(ok5); // macro
    static_assert(ok6); // singleton (macro also)

    // Display
    std::cout << ok1 << std::endl;
    std::cout << ok2 << std::endl;
    std::cout << ok3 << std::endl;
    std::cout << ok4 << std::endl;
    std::cout << ok5 << std::endl;
    std::cout << ok6 << std::endl;

    // Return
    return 0;
}

это скомпилировано с текущей транковой версией GCC с параметрами -std=c++2a. Смотрите результат здесь в проводнике компилятора.


Тот факт, что ok0, ok5 и ok6 работают, на самом деле не является сюрпризом. Однако тот факт, что ok2 и ok3 равны true, а ok4 не является для меня очень удивительным.

  • Может ли кто-нибудь дать объяснение правил, которые делают ok3 true, но ok4 false?
  • Это действительно так, как это должно работать, или это ошибка компилятора, связанная с экспериментальной функцией (лямбда-выражения в неоцененных контекстах)? (ссылка на стандарт или предложения C ++ приветствуются)

Примечание: я действительно надеюсь, что это функция, а не ошибка, но только потому, что она делает некоторые безумные идеи реализуемыми

Ответы [ 2 ]

2 голосов
/ 14 мая 2019

Может ли кто-нибудь дать объяснение правил, которые делают ok3 истинным, а ok4 ложным?

ok3 - истиной, поскольку в качестве типа по умолчанию используется лямбда-тип.

Тип лямбда-выражения (который также является типом объекта замыкания) является уникальным , безымянный тип класса без объединения,

Следовательно, тип шаблона по умолчанию для object, тип параметра шаблона для macro и singltone всегда различаются после каждой установки.Но для функции function возвращаемый вызов lambda уникален, а его тип уникален.Функция шаблона ctad имеет шаблон только для параметров, но возвращаемое значение является уникальным.Если переписать функцию как:

template <class... Args, class T =  decltype([]{})>
ctad(const Args&...) -> ctad<T>;

В этом случае тип возвращаемого значения будет отличаться после каждого экземпляра.

0 голосов
/ 18 апреля 2019

для типа параметра функции ok2 (T) зависит от указанного параметра шаблона. для ok3 ctor это не шаблон.

для ok4 оба вычета зависят от одного и того же списка типов параметров (который в данном случае пуст), и из-за этого вычет происходит только один раз. Создание шаблона и дедукция - разные вещи. в то время как для одного и того же типа параметра вывод списка происходит только один раз, создание экземпляра происходит для всех случаев использования.

посмотрите этот код (https://godbolt.org/z/ph1Wk2)., если параметры для вычета отличаются, происходят отдельные вычеты.

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