英文:
Check all parameter pack args of function template belong to int
问题
I want my function to accept variadic args of type int
only. For example, to compute sum (taken from C++ core guidelines):
template<class ...Args>
auto sum(Args... args) // GOOD, and much more flexible
{
return (... + args); // note: C++17 "fold expression"
}
Taking this simple example from std::conjunction -
template<typename T, typename... Ts>
std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...>, int>
control(T, Ts...)
{
std::cout << "all types in pack are T\n";
return 0;
}
seems to check type T against Ts, which isn't something I want (it will compile successfully if all args of type say double
).
How do I go about doing that?
Secondly, does the conjunction example split T and Ts just for the purpose of checking types, and it's intended usage is just one set of variadic args? What is this pattern called?
英文:
I want my function to accept variadic args of type int
only. For example, to compute sum (taken from C++ core guidelines):
template<class ...Args>
auto sum(Args... args) // GOOD, and much more flexible
{
return (... + args); // note: C++17 "fold expression"
}
Taking this simple example from std::conjunction -
template<typename T, typename... Ts>
std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...>, int>
control(T, Ts...)
{
std::cout << "all types in pack are T\n";
return 0;
}
seems to check type T against Ts, which isn't something I want (it will compile successfully if all args of type say double
).
How do I go about doing that?
Secondly, does the conjunction example split T and Ts just for the purpose of checking types, and it's intended usage is just one set of variadic args? What is this pattern called?
答案1
得分: 1
你可以在 enable_if
中使用折叠表达式:
template<typename... Args,
std::enable_if_t<(... && std::is_same_v<Args, int>), bool> = true>
auto sum(Args... args) // 良好,更加灵活
{
return (... + args); // 注意:C++17 中的 "fold expression"
}
英文:
You can use a fold expression in the enable_if
:
template<typename... Args,
std::enable_if_t<(... && std::is_same_v<Args, int>), bool> = true>
auto sum(Args... args) // GOOD, and much more flexible
{
return (... + args); // note: C++17 "fold expression"
}
答案2
得分: 1
Here is the translated code segment you requested:
如果您想要一个仅接受可变数量的`int`的可变参数函数,可以使用`std::enable_if`中的折叠表达式来实现:
```cpp
template<class ...Args>
auto sum(Args... args)
-> std::enable_if_t<(... && std::is_same_v<Args, int>), int>
// 注意:最好在返回类型中使用 std::enable_if_t。
// 实际返回类型是int,std::enable_if_t只是为了进行SFINAE。
{
// 注意:使用零以获取空包的值
return (0 + ... + args);
}
在这里使用std::conjunction
是不必要的,特别是在C++17中,折叠表达式同样可以胜任。是的,将包分为T
和Ts
只是为了能够通过std::is_same_v<T, Ts>...
将所有Ts
与第一个类型进行比较。
然而,这个设计非常值得质疑,因为:
- 这将实例化单独的函数
sum<int>
,sum<int, int>
,...(对于编译性能不利) - 我们不能使用
long
或float
类型的参数调用此函数,因为替换将失败,即使它们是常数,并且我们知道转换为int
是安全的
一个更合理的设计是使用std::initializer_list<int>
:
#include <numeric>
int sum(std::initializer_list<int> args)
{
return std::reduce(args.begin(), args.end());
}
// 使用 sum({1, 2, 3, 4}) 调用
是的,这将需要我们使用额外的大括号,但隐式转换可以正常工作,并且该函数甚至不再是一个模板,因此是值得的。
<details>
<summary>英文:</summary>
If you want a variadic function that only accepts a variable amount of `int`s, a fold expression in `std::enable_if` could do that:
```cpp
template<class ...Args>
auto sum(Args... args)
-> std::enable_if_t<(... && std::is_same_v<Args, int>), int>
// note: It's best to use std::enable_if_t in return types.
// The actual return type is int here, std::enable_if_t is just wrapping it
// for the sake of doing SFINAE.
{
// note: use zero to get a value for empty packs
return (0 + ... + args);
}
Using std::conjunction
is overkill here, especially since fold expressions get the job done just as well in C++17. And yes, splitting the pack into T
and Ts
was only done so that we can compare all Ts
to the first type via std::is_same_v<T, Ts>...
.
However, this is really questionably design, because:
- this will instantiate separate functions
sum<int>
,sum<int, int>
, ... (bad for compilation performance) - we cannot call this function with arguments of type
long
orfloat
, because substitution would fail, even if they are constants and we know that converting toint
is safe
A much saner design would be to use std::initializer_list<int>
:
#include <numeric>
int sum(std::initializer_list<int> args)
{
return std::reduce(args.begin(), args.end());
}
// call with sum({1, 2, 3, 4})
Yes, this will require us to use extra braces, but implicit conversions work, and the function is not even a template anymore, making it worth it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论