CTAD, initializer_list, неявный конструктор и вызов функции - PullRequest
0 голосов
/ 22 февраля 2019

По сути, я хочу что-то вроде multi_types std::initializer_list

template<typename ...As>
struct Foo {
Foo(As...){}
};

template<typename ...As>
void f(Foo<As...>) {}

int main() {
  f(Foo{5, 3.f}); // 1) compile
  f({}); // 2) compile
  f({5, 3}); // 3) error
  f({5, 3.8}); // 4) error
  return 0;
}

Я понимаю, почему компилируется первый пример.Однако я не понимаю, почему второй компилируется, а другие нет.Для меня второе также не должно компилироваться, если третье и четвертое не компилируются.Есть ли способ заставить третий и четвертый компилировать?

Ответы [ 2 ]

0 голосов
/ 17 марта 2019

Есть ли способ заставить третий и четвертый компилировать?

Я думаю, что нет способа изменить f или Foo, чтобы ваш четвертый пример компилировался.

Чтобы ваш третий пример компилировался, вы можете добавить перегрузку f, которая принимает массив:

#include <cstddef>
#include <utility>

#define EXPLICIT_TEMPLATE_ARGUMENTS 1
//#define EXPLICIT_TEMPLATE_ARGUMENTS 0

#if EXPLICIT_TEMPLATE_ARGUMENTS
template<class T, std::size_t>
struct identity {
    using type = T;
};
#endif

template<class ...As>
struct Foo {
    Foo(As...);
};

template<class ...As>
void f(Foo<As...>);

template<class A, std::size_t N, std::size_t ...Indexes>
void f_helper(A const (&as)[N], std::index_sequence<Indexes...>) {
    static_assert(N == sizeof...(Indexes));
#if EXPLICIT_TEMPLATE_ARGUMENTS
    // Example pack expansions:
    // return f<>(Foo<>{});
    // return f<A>(Foo<A>{as[0]});
    // return f<A, A>(Foo<A, A>{as[0], as[1]});
    return f<typename identity<A, Indexes>::type...>(Foo<typename identity<A, Indexes>::type...>{as[Indexes]...});
#else
    // Example pack expansions:
    // return f(Foo{});
    // return f(Foo{as[0]});
    // return f(Foo{as[0], as[1]});
    return f(Foo{as[Indexes]...});
#endif
}

// Call as: f({x, y, z})
template<class A, std::size_t N>
void f(A const (&as)[N]) {
    return f_helper<A, N>(as, std::make_index_sequence<N>());
}

int main() {
  f(Foo{5, 3.f}); // 1) compile
  f({}); // 2) compile
  f({5, 3}); // 3) compile (was: error)
  f({5, 3.8}); // 4) error

  return 0;
}
0 голосов
/ 17 марта 2019

Однако я не понимаю, почему второй компилируется, а другие не [компилируют].

Примеры 3 и 4 не компилируются, поскольку выведенные аргументы шаблона отличаются отчто вы ожидаете.

Когда вы записываете вызов (f({5, 3})) в шаблон функции template<typename ...As> void f(Foo<As...>), компилятор должен вывести все пропущенные аргументы шаблона (...As).Эти отсутствующие аргументы шаблона сначала выводятся путем сравнения аргументов функции ({5, 3}) с параметрами функции (Foo<As...>).Если аргумент функции является списком инициализатора ({5, 3}) [1], вывод аргумента шаблона пропускается для параметра функции (Foo<As...>):

N4659
[temp.deduct.call] 17.8.2.1 (1):

[...] аргумент списка инициализатора приводит к тому, что параметр считается недоступным контекстом (17.8.2.5).

N4659
[temp.deduct.type] 17.8.2.5 (5.6):

[Неведедированный контекст включает в себя] параметр функции, для которого связанный аргумент является списком инициализатора (11.6.4) [...]

Поскольку пакет параметров конечного шаблона (As...) не выводится никакими аргументами функции, он выводится как пустой:

N4659
[temp.arg.explicit] 17.8.1 (3):

Пакет параметров конечного шаблона (17.5.3), не выведенный иным образом, будет выведен в пустую последовательность аргументов шаблона.

Для f({5, 3}) компилятор выводит, что аргументы шаблона <> (пустые), таким образом специализация вызываетсяd составляет void f(Foo<>).Важно: эта специализация выбирается независимо от того, что находится внутри списка инициализатора.Это объясняет диагностику, предоставленную компилятором:

Clang:
ошибка: нет соответствующей функции для вызова 'f'
примечание: функция-кандидат [с As = <>] не жизнеспособна: невозможно преобразовать аргумент списка инициализатора в 'Foo <>'

GCC:
ошибка: не удалось преобразовать '{5, 3}' из '' в 'Foo <>'

MSVC:
ошибка C2664: «void f <> (Foo <>)»: невозможно преобразовать аргумент 1 из «списка инициализаторов» в «Foo <>»
примечание: ни один конструктор не может принять тип источника или конструкторразрешение перегрузки было неоднозначным

При вызове функции параметры функции инициализируются с использованием copy-initialization из аргументов функции.Следующие операторы инициализируют переменную с использованием тех же правил, что и ваши примеры инициализируют параметр f:

Foo<> x = {};        // Example 2
Foo<> x = {5, 3};    // Example 3
Foo<> x = {5, 3.8};  // Example 4

[1] и, вкратце, если параметр функции не является ни std::initializer_list<T>, ни T[n]

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