是否可以编写模板化类,以便成员函数的签名/原型取决于模板参数?例如,如果我声明一个带有foo
N个模板参数的类int
,则成员函数foo::bar()
的原型是否可能具有N个相同类型的参数?我知道我只是将std::array
或std::vector
作为参数传递,但是在应用程序中,我认为这是行不通的。
template <int N>
class foo {
std::array<float, N> _data;
public:
foo() {};
~foo() {};
void bar(float arg0, float arg1, float arg2 /*, ... */) const;
};
自C ++ 17起,您拥有constexpr-if,而自C ++ 11起,您拥有static_assert。它们可以一起用来构建您想要的东西。我在这里没有考虑您的std::array
,因为我不了解它的用法。这只是汇总传递给bar
的参数:
template<size_t N, typename T = float>
class foo {
private:
// recursive instantiation
template<size_t Depth, typename... Args>
T rebar(T f, Args... args) const {
if constexpr(Depth>1) return f + rebar<Depth-1>(args...);
else return f;
}
public:
template<typename... Args>
T bar(Args... args) const {
// killing all overloads with the wrong number of arguments
static_assert(sizeof...(args)==N, "wrong number of arguments");
return rebar<N>(args...);
}
};
但是,当然! (std::conjunction
需要C ++ 17)
template<int N>
class foo {
public:
template<typename T, typename... Args,
typename = std::enable_if_t<
std::conjunction_v<
std::is_same<T, Args>...
>
&& sizeof...(Args) + 1 == N
>
>
void bar(T first, Args... args) {
std::cout << first;
((std::cout << ", " << args), ..., (std::cout << std::endl));
//do stuff
};
};
int main() {
foo<3> f;
f.bar(1,2,3);
//prints 1, 2, 3
}
typename T
声明函数的第一个参数的类型。
typename... Args
声明包含一些自变量的参数包类型
[std::enable_if_t< val >
如果val
为真,则启用此功能,如果val
为假,则禁用此功能
[std::conjunction
要求传递给它的参数列表中的所有参数都为true才能返回true,aka std::conjunction<val1,val2,val3>
大致等同于(val1 && val2 && val3)
,尽管所有val
都必须是constexpr。
如果std::is_same<T,U>
和true
是同一类型,则[T
返回U
。
std::is_same<T,Args>...
将参数包扩展为...std::is_same<T,Arg0>,std::is_same<T,Arg2>, etc..., std::is_same<T,ArgN>
因此,我们将其传递回去
[std::conjunction<std::is_same<T,Arg0>,std::is_same<T,Arg2>, etc..., std::is_same<T,ArgN>>
,如果所有参数都相同,则评估为true
sizeof...(Args)
返回Args
中的参数数量因此,由于我们将T
作为参数之一,因此sizeof...(Args) + 1
返回传递给该函数的参数总数。
因此,如果该函数的参数总数为sizeof...(Args) + 1 == N
,则true
返回N
。
结合两者,
std::conjunction_v<std::is_same<T, Args>...>
&& sizeof...(Args) + 1 == N
如果所有参数都是同一类型,并且总共有true
个参数,将返回N
。
最后,如果通过将结果评估为std::enable_if_t
,则启用了此功能
typename = std::enable_if_t<
std::conjunction_v<
std::is_same<T, Args>...
>
&& sizeof...(Args) + 1 == N
>
将其包装起来,我们有
template<typename T, typename... Args,
typename = std::enable_if_t<
std::conjunction_v<
std::is_same<T, Args>...
>
&& sizeof...(Args) + 1 == N
>e
>
void bar(T first, Args... args) {
std::cout << first;
((std::cout << ", " << args), ..., (std::cout << std::endl));
//do stuff
};
编辑:@Ted Lyngmo可能有更好的答案来使用static_assert
,尤其是因为它提供了更好的错误消息。我将在此处发布此内容只是为了展示如何也具有“所有相同类型”的部分。
template<int N>
class foo {
template<typename T, typename... Args>
void barImpl(T first, Args... args) {
//do stuff
}
public:
template<typename T, typename... Args>
void bar(T first, Args... args) {
static_assert(std::conjunction_v<std::is_same<T, Args>...>, "all arguments must be the same type!");
static_assert(sizeof...(Args) + 1 == N, "Must have exactly N arguments!");
barImpl(std::forward<T>(first), std::forward<Args>(args)...);
};
};