[英]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. 我认为没有办法改变f
或Foo
以使你的第四个例子编译。
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.