简体   繁体   English

CTAD,initializer_list,非显式构造函数,以及对函数的调用

[英]CTAD, initializer_list, non explicit constructor, and call to a function

Basically, what I want is something like multi_types std::initializer_list 基本上,我想要的是像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;
}

I do understand why the first example compiles. 我明白为什么第一个例子编译。 However, I do not understand why the second compiles but the others don't. 但是,我不明白为什么第二次编译但其他编译没有。 To me, the second should also not compile if third and fourth do not compile. 对我来说,第二个也不应该编译,如果第三个和第四个不编译。 Is there a way to make third and fourth compile? 有没有办法进行第三次和第四次编译?

However, I do not understand why the second compiles but the others don't [compile]. 但是,我不明白为什么第二次编译但其他编译没有[编译]。

Examples 3 and 4 fail to compile because the deduced template arguments are different from what you expect. 示例3和4无法编译,因为推导出的模板参数与您的预期不同。

When you write a call ( f({5, 3}) ) to a function template template<typename ...As> void f(Foo<As...>) , the compiler needs to deduce any missing template arguments ( ...As ). 当您向函数模板template<typename ...As> void f(Foo<As...>)编写调用( f({5, 3}) template<typename ...As> void f(Foo<As...>) f({5, 3}) template<typename ...As> void f(Foo<As...>) ,编译器需要推导出任何缺少的模板参数( ...As )。 These missing template arguments are first deduced by comparing the function arguments ( {5, 3} ) with the function parameters ( Foo<As...> ). 首先通过将函数参数( {5, 3} )与函数参数( Foo<As...> )进行比较来推导出这些缺少的模板参数。 If a function argument is an initializer list ( {5, 3} ) [1], template argument deduction is skipped for the function parameter ( Foo<As...> ): 如果函数参数是初始化列表( {5, 3} )[1], 则跳过函数参数Foo<As...> )的模板参数推导

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

[...] an initializer list argument causes the parameter to be considered a non-deduced context (17.8.2.5). [...]初始化列表参数使参数被视为非推导上下文(17.8.2.5)。

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

[Non-deduced contexts include a] function parameter for which the associated argument is an initializer list (11.6.4) [...] [非推导的上下文包括]关联参数是初始化列表的函数参数(11.6.4)[...]

Because the trailing template parameter pack ( As... ) is not deduced by any function arguments, it is deduced as empty: 因为任何函数参数都没有推导出尾随模板参数包( As... ),所以它被推断为空:

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

A trailing template parameter pack (17.5.3) not otherwise deduced will be deduced to an empty sequence of template arguments. 未以其他方式推导出的尾随模板参数包(17.5.3)将被推导为空的模板参数序列。

For f({5, 3}) , the compiler deduces that the template arguments are <> (empty), thus the specialization being called is void f(Foo<>) . 对于f({5, 3}) 5,3 f({5, 3}) ,编译器推断模板参数是<> (空),因此被调用的特化是void f(Foo<>) Important: This specialization is chosen regardless of what's inside the initializer list. 重要事项:无论初始化程序列表中包含什么内容,都会选择此专门化。 This explains the diagnostics given by the compiler: 这解释了编译器提供的诊断:

Clang: 铛:
error: no matching function for call to 'f' 错误:没有匹配函数来调用'f'
note: candidate function [with As = <>] not viable: cannot convert initializer list argument to 'Foo<>' 注意:候选函数[with As = <>]不可行:无法将初始化列表参数转换为'Foo <>'

GCC: GCC:
error: could not convert '{5, 3}' from '' to 'Foo<>' 错误:无法将'{5,3}'从''转换为'Foo <>'

MSVC: MSVC:
error C2664: 'void f<>(Foo<>)': cannot convert argument 1 from 'initializer list' to 'Foo<>' 错误C2664:'void f <>(Foo <>)':无法将参数1从'初始化列表'转换为'Foo <>'
note: No constructor could take the source type, or constructor overload resolution was ambiguous 注意:没有构造函数可以采用源类型,或构造函数重载解析是不明确的

In a function call, function parameters are initialized using copy-initialization from the function arguments. 在函数调用中,函数参数使用函数参数的复制初始化进行初始化 The following statements initialize a variable using the same rules that your examples initialize the parameter of f : 以下语句使用与示例初始化f参数相同的规则初始化变量:

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

[1] and, in short, if the function parameter is neither std::initializer_list<T> nor T[n] [1],简而言之,如果函数参数既不是std::initializer_list<T>也不是T[n]

Is there a way to make third and fourth compile? 有没有办法进行第三次和第四次编译?

I think there is no way to change f or Foo to make your fourth example compile. 我认为没有办法改变fFoo以使你的第四个例子编译。

To make your third example compile, you can add an overload of f which takes an array: 要使您的第三个示例编译,您可以添加一个带有数组的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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM