英文:
Why I need default sum function, for a variadic template sum?
问题
我想计算给定给函数sum
的任意数量的参数的总和。假设给函数的整数将满足operator+
。
如果我注释掉函数sum()
(没有参数的那个函数),则代码无法编译。如果我取消注释它,代码会编译并运行,但永远不会调用函数sum()
。
我似乎无法理解为什么我们需要sum()
函数,因为我正在使用sizeof...(Args)
上的条件。
如果有人能帮助我理解这一点,我会非常感激。
/*
int sum()
{
std::cout << "Sum with 0 Args" << std::endl;
return 0;
}
*/
template <typename T, typename...Args>
T sum(T first, Args...args)
{
// std::cout << sizeof...(Args) << std::endl;
if (sizeof...(Args) != 0)
{
return first + sum(args...);
}
else
{
std::cout << "Found 0 args" << std::endl;
return first;
}
}
int main()
{
std::cout << sum(1, 2, 3) << std::endl;
std::cout << sum(1.2, 3.5) << std::endl;
return 0;
}
一旦我取消注释sum()
函数,我会得到以下输出 -
Found 0 args
6
Found 0 args
4.7
基本上,sum()
永远不会被调用,这是预期的,但为什么我们需要它呢?
英文:
I want to calculate the sum of any number of arguments given to function sum
. Assuming that integers given to the function will satisfy operator+
.
If I comment out the function sum()
(the one having no arguments), the code does not compile. And if I uncomment it, the code does compile and run, but never hits the function sum()
.
I can't seem to understand that why do we need to have sum()
function at all as I am using condition on sizeof...(Args)
Will really appreciate it if someone can help me understand this?
/*
int sum()
{
std::cout << "Sum with 0 Args" << std::endl;
return 0;
}
*/
template <typename T, typename...Args>
T sum(T first, Args...args)
{
// std::cout << sizeof...(Args) << std::endl;
if (sizeof...(Args) != 0)
{
return first + sum(args...);
}
else
{
std::cout << "Found 0 args" << std::endl;
return first;
}
}
int main()
{
std::cout << sum(1, 2, 3) << std::endl;
std::cout << sum(1.2, 3.5) << std::endl;
return 0;
}
Once I uncomment function sum(), I get below output -
Found 0 args
6
Found 0 args
4.7
Basically sum()
never get called which is expected, but then why do we need it in the first place?
答案1
得分: 5
基本上 sum()
从未被调用,这是预期的,但我们一开始为什么需要它呢?
当你使用普通的 if
语句时,就像在你的代码中一样,两个分支都会被检查,两者都必须可以编译。当递归调用 sum
时,最后一次函数调用没有参数。为了使这种情况成立,编译器需要一个没有参数的 sum
函数。
另一方面,自 [tag:C++17] 起,我们有了 if constexpr
,通过它我们可以在编译时只保留真正的分支。这意味着,只需将代码更改如下,你就不再需要 sum()
了。
在这里阅读更多:if constexpr
与 if
之间的区别?
template <typename T, typename...Args>
T sum(T first, Args...args)
{
if constexpr (sizeof...(Args) != 0)
{
// .... 和以前一样
}
else {
// .... 和以前一样
}
}
然而,在 [tag:C++11] 或 [tag:C++14] 中,你可以通过稍微巧妙/狡猾的方式使用单一的 sum
函数,使用了 <sup>†</sup> 折叠表达式:
#include <type_traits> // std::common_type
template <typename...Args>
auto sum(Args...args) -> typename std::common_type<Args...>::type
{
using unused = int[];
typename std::common_type<Args...>::type total{};
return static_cast<void>(unused{ 0, (total += args, 0)...}), total;
}
<sup>†</sup> 参考:
英文:
>Basically sum()
never get called which is expected but then why do we need it in the first place?
When you use the normal if
statement, like in your code, the both branches are checked, and both must be compilable. When the sum
is called recursively, the last function call is with no arguments. For this case to be true, the compiler requires a sum
function with no arguments.
On the other hand, since [tag:C++17], we have if constexpr
, by which we can retain only the true branches at compile time. That means, just changing the code as follows, you no longer required the sum()
with no argument anymore.
Read more here: Difference between if constexpr
and if
?
template <typename T, typename...Args>
T sum(T first, Args...args)
{
if constexpr (sizeof...(Args) != 0)
// ^^^^^^^^^^
{
// .... as before
}
else {
// .... as before
}
}
However, in [tag:C++11] or [tag:C++14], you may achieve a single sum
function, by slightly tricky/ hacky <sup>†</sup> fold expression:
#include <type_traits> // std::common_type
template <typename...Args>
auto sum(Args...args) -> typename std::common_type<Args...>::type
{
using unused = int[];
typename std::common_type<Args...>::type total{};
return static_cast<void>(unused{ 0, (total += args, 0)...}), total;
}
See a live demo in godbolt.org
<sup>†</sup> References:
答案2
得分: 0
从C++17开始,您可以在递归位中使用折叠表达式,并且必须使用if constexpr
来进行编译时条件判断。
#include <iostream>
template<typename... args_t>
auto sum(args_t&&... args)
{
if constexpr (sizeof...(args_t) == 0)
{
return 0;
}
else
{
return (args + ...);
}
}
int main()
{
std::cout << sum() << "\n";
std::cout << sum(1, 2, 3);
return 0;
}
英文:
From C++17 you can use a fold expression for you recursive bit, and you have to use if constexpr for compile time if.
#include <iostream>
template<typename... args_t>
auto sum(args_t&&... args)
{
if constexpr (sizeof...(args_t) == 0)
{
return 0;
}
else
{
return (args + ...);
}
}
int main()
{
std::cout << sum() << "\n";
std::cout << sum(1, 2, 3);
return 0;
}
答案3
得分: 0
我之前漏掉了对C++11的要求。以下是一个应该与C++兼容的示例。演示链接在这里:https://onlinegdb.com/js_WXEeiC
#include <iostream>
namespace details
{
template<typename arg1_t>
inline constexpr bool same_type()
{
return true;
}
template<typename arg1_t, typename arg2_t, typename... args_t>
inline constexpr bool same_type()
{
return std::is_same<arg1_t, arg2_t>::value && same_type<arg2_t, args_t...>();
}
template<typename arg_t>
arg_t sum(arg_t value)
{
return value;
}
template<typename arg_t, typename... args_t>
arg_t sum(arg_t value, args_t... values)
{
return value + sum(std::forward<args_t>(values)...);
}
}
template<typename arg_t, typename... args_t>
auto sum(arg_t value, args_t... values)
-> typename std::enable_if<details::same_type<arg_t, args_t...>(), arg_t>::type
{
return details::sum(value, values...);
}
int main()
{
static_assert(details::same_type<int>());
static_assert(details::same_type<int, int>());
static_assert(details::same_type<int, int, int>());
static_assert(!details::same_type<int, int, double>());
std::cout << sum(1, 2, 3);
return 0;
}
英文:
I missed the C++11 requirement earlier. Here is an example that should be C++ compatible. Demo here : https://onlinegdb.com/js_WXEeiC
#include <iostream>
namespace details
{
template<typename arg1_t>
inline constexpr bool same_type()
{
return true;
}
template<typename arg1_t, typename arg2_t, typename... args_t>
inline constexpr bool same_type()
{
return std::is_same<arg1_t, arg2_t>::value && same_type<arg2_t, args_t...>();
}
template<typename arg_t>
arg_t sum(arg_t value)
{
return value;
}
template<typename arg_t, typename... args_t>
arg_t sum(arg_t value, args_t... values)
{
return value + sum(std::forward<args_t>(values)...);
}
}
template<typename arg_t, typename... args_t>
auto sum(arg_t value, args_t... values)
-> typename std::enable_if<details::same_type<arg_t, args_t...>(), arg_t>::type
{
return details::sum(value, values...);
}
int main()
{
static_assert(details::same_type<int>());
static_assert(details::same_type<int, int>());
static_assert(details::same_type<int, int, int>());
static_assert(!details::same_type<int, int, double>());
std::cout << sum(1, 2, 3);
return 0;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论