为什么我需要默认的求和函数,用于可变模板求和?

huangapple go评论108阅读模式
英文:

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 &lt;&lt; &quot;Sum with 0 Args&quot; &lt;&lt; std::endl;
    return 0;
}
*/

template &lt;typename T, typename...Args&gt;
T sum(T first, Args...args) 
{
    // std::cout &lt;&lt; sizeof...(Args) &lt;&lt; std::endl;
    if (sizeof...(Args) != 0) 
    {
        return first + sum(args...);
    }
    else 
    {
        std::cout &lt;&lt; &quot;Found 0 args&quot; &lt;&lt; std::endl;
        return first;
    }
}

int main()
{
    std::cout &lt;&lt; sum(1, 2, 3) &lt;&lt; std::endl;
    std::cout &lt;&lt; sum(1.2, 3.5) &lt;&lt; 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 constexprif 之间的区别?

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;
}

在 godbolt.org 上查看实时演示

<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 &lt;typename T, typename...Args&gt;
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 &lt;type_traits&gt; // std::common_type

template &lt;typename...Args&gt;
auto sum(Args...args) -&gt; typename std::common_type&lt;Args...&gt;::type
{
    using unused = int[];
    typename std::common_type&lt;Args...&gt;::type  total{};
    return static_cast&lt;void&gt;(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 &lt;iostream&gt;

template&lt;typename... args_t&gt;
auto sum(args_t&amp;&amp;... args)
{
	if constexpr (sizeof...(args_t) == 0)
	{
		return 0;
	}
	else
	{
		return (args + ...);
	}
}

int main()
{
	std::cout &lt;&lt; sum() &lt;&lt; &quot;\n&quot;;
	std::cout &lt;&lt; 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 &lt;iostream&gt;

namespace details
{
	template&lt;typename arg1_t&gt;
	inline constexpr bool same_type()
	{
		return true;
	}

	template&lt;typename arg1_t, typename arg2_t, typename... args_t&gt;
	inline constexpr bool same_type()
	{
		return std::is_same&lt;arg1_t, arg2_t&gt;::value &amp;&amp; same_type&lt;arg2_t, args_t...&gt;();
	}

	template&lt;typename arg_t&gt;
	arg_t sum(arg_t value)
	{
		return value;
	}


	template&lt;typename arg_t, typename... args_t&gt;
	arg_t sum(arg_t value, args_t... values)
	{
		return value + sum(std::forward&lt;args_t&gt;(values)...);
	}
}

template&lt;typename arg_t, typename... args_t&gt;
auto sum(arg_t value, args_t... values)
-&gt; typename std::enable_if&lt;details::same_type&lt;arg_t, args_t...&gt;(), arg_t&gt;::type
{
	return details::sum(value, values...);
}

int main()
{
	static_assert(details::same_type&lt;int&gt;());
	static_assert(details::same_type&lt;int, int&gt;());
	static_assert(details::same_type&lt;int, int, int&gt;());
	static_assert(!details::same_type&lt;int, int, double&gt;());


	std::cout &lt;&lt; sum(1, 2, 3);
	return 0;
}

huangapple
  • 本文由 发表于 2023年7月31日 23:20:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76805009.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定