在C++20中实现可变参数的Max函数

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

Implementing variadic Max function in C++20

问题

I think the error is coming from the function call: Max("string1", "string2"s));. I do not know, how can resolve this.
Likewise, I am also feeling that I am writing more to achieve this Max function in [tag:C++20]. Does anybody have any suggestion to make the two Max functions into one?

英文:

Despite, the fact, we have std::max, I wanted to try if it is possible to make a Max version that takes variadic arguments and calls the Max recursively for finding the max element.

I saw similar posts in stack overflow, but those are old and most of them use std::max inside. Since I have a specific error and using a newer compiler, this post is not duplicated easily.

Following is the code I have written:

#include <iostream>
#include <string>
#include <format>
using namespace std::string_literals;

template <typename T>
constexpr T Max(T&& value)
{  
  return value;
}

template <typename T, typename... Ts>
constexpr T Max(T&& value, Ts&&... args)
{
    const T maxRest = Max(args...);

    return (value > maxRest) ? value : maxRest;
}

int main()
{
    std::cout << std::format("Maximum integer: {}\n", Max(1));
    std::cout << std::format("Maximum integer: {}\n", Max(5, 2, 10, 6, 8));
    std::cout << std::format("Maximum integer: {}\n", Max("string1", "string2"s));  // error in this line!!
    std::cout << std::format("Maximum double: {}\n", Max(3.14, 1.23, 2.56, 0.98));
    return 0;
}

For which I am getting:

main.cc(79, 21) : error C2440 : 'initializing' : cannot convert from 'std::string' to 'const char (&)[8]'
main.cc(79, 21) : message: Reason: cannot convert from 'std::string' to 'const char [8]'
main.cc(79, 21) : message: No user - defined - conversion operator available that can perform this conversion, or the operator cannot be called
main.cc(87, 55) : message: see reference to function template instantiation 'T Max<const char(&)[8],std::string>(T,std::string &&)' being compiled
with
[
    T = const char(&)[8]
]
  • I think the error is coming from the function call: Max("string1", "string2"s));. I do not know, how can resolve this.
  • Likewise, I am also feeling that I am writing more to achieve this
    Max function in [tag:C++20]. Does anybody have any suggestion to
    make the two Max functions into one?

答案1

得分: 13

同样,我也感到我正在更多地写代码来实现这个 Max 函数在 [tag:C++20] 中[...]?

您的 Max 函数可以通过以下方式最大化简洁性:

constexpr auto Max(auto const& value, auto const&... args)
{
    if constexpr (sizeof...(args) == 0u) // 单参数情况!
        return value;
    else // 对于 Ts...
    {
        const auto max = Max(args...);
        return value > max ? value : max;
    }
}

在godbolt.org上查看实时演示

更新:正如 @TedLyngmo 在评论部分指出的,如果您只传递连续的 const char*(字符串字面值),上述方法将无效。例如,情景如下:

Max("string1"s, "string2", "string4", "string3") // 结果是 "string2" 而不是 "string4"

因为这将导致指针比较,而不是您想要的比较。这也适用于您原来展示的代码。您可能需要单独处理这种情况。

例如,在以下代码示例中,如果 value 可转换为 std::string_view,我们将其转换为 std::string_view 并进行比较:

#include <type_traits>  // std::is_convertible

constexpr auto Max(auto const& value, auto const&... args)
{
    if constexpr (sizeof...(args) == 0u) // 单参数情况!
    {
        if constexpr (std::is_convertible_v<decltype(value), std::string_view>)
            return std::string_view{value};
        else
            return value;
    }
    else // 对于 Ts...
    {
        const auto max = Max(args...);
        return value > max ? value: max;
    }
}

在godbolt.org上查看实时演示

同样,每次使用这个 Max 函数时,请始终记住检查传递的参数是否是某种指针类型,因为这不明显由它处理。


我认为错误来自函数调用:Max("string1", "string2"s));
我不知道如何解决这个问题。

当您调用 Max("string1", "string2"s)); 时,编译器推断 T(即返回类型)为 const char[8],即第一个参数的类型(即 &quot;string1&quot;)。然而,第二个参数是一个 std::string(即 &quot;string2&quot;s)。现在对于以下代码行:

const T maxRest = Max(args...);

这个 std::string 必须隐式转换为 const char [8]。这是不可行的,因此编译器生成了类型不匹配错误。

要解决此问题,您可以简单地让编译器为您推断类型;这意味着,不要定义或假设返回类型将始终是 T,而是使用 auto,以便编译器可以为您推断类型。

template <typename T, typename... Ts>
constexpr auto Max(T const& value, Ts const&... args)
//        ^~~~ ---> 简单地 'auto'
{
    const auto maxRest = Max(args...);
    //    ^~~~ ---> 简单地 'auto'
    return (value > maxRest) ? value : maxRest;
}

在godbolt.org上查看实时演示

或者,您还可以使用 std::common_type_t 来定义返回类型。

#include <type_traits> // std::common_type_t

template <typename T, typename... Ts>
constexpr auto Max(T const& value, Ts const&... args)
-> std::common_type_t<T, Ts...>
{
    // ....
}
英文:

> Likewise, I am also feeling that I am writing more to achieve this Max function in [tag:C++20] [...] ?

Your Max function can maximize the simplicity by

constexpr auto Max(auto const&amp; value, auto const&amp;... args)
{
    if constexpr (sizeof...(args) == 0u) // Single argument case!
        return value;
    else // For the Ts...
    {
        const auto max = Max(args...);
        return value &gt; max ? value : max;
    }
}

See live demo in godbolt.org

Update: As pointed out in the comment section by @TedLyngmo, the above does not work if you only pass consecutive const char*s (string literals). Ex. scenario

Max(&quot;string1&quot;s, &quot;string2&quot;, &quot;string4&quot;, &quot;string3&quot;) // result is &quot;string2&quot; instead of &quot;string4&quot;

Because this is resulting a pointer comparison rather than the comparison you want. It was the case also for your original shown code. You might want to handle this situation separately.

For instance, in the following code example, if the value is convertible to std::string_view, we convert it to std::string_view and do the greater check:

#include &lt;type_traits&gt;  // std::is_convertible

constexpr auto Max(auto const&amp; value, auto const&amp;... args)
{
    if constexpr (sizeof...(args) == 0u) // Single argument case!
    {
        if constexpr (std::is_convertible_v&lt;decltype(value), std::string_view&gt;)
            return std::string_view{value};
        else
            return value;
    }
    else // For the Ts...
    {
        const auto max = Max(args...);
        return value &gt; max ? value: max;
    }
}

See live demo in godbolt.org

Likewise, each time when you use this Max function, always remember to check whether the passed argument is some type of pointer, which is clearly not handled by it.


> I think the error is coming from the function call: Max(&quot;string1&quot;, &quot;string2&quot;s)); .
> I do not know, how can to resolve this.

When you call Max(&quot;string1&quot;, &quot;string2&quot;s)), the compiler deduces the T (i.e. return type), to be
const char[8], that is the type of the first argument(i.e. &quot;string1&quot;). However, the second argument is a std::string (i.e. &quot;string2&quot;s). Now for the line :

const T maxRest = Max(args...);

this std::string must be now implicitly convertible to const char [8].This is not viable, and hence the compiler produces a type mismatch error.

To fix the issue, you can simply let the compiler deduce the type for you; That means, instead of defining or assuming the
the return type will be always T, use auto so that compiler can deduce the type for you.

template &lt;typename T, typename... Ts&gt;
constexpr auto Max(T const&amp; value, Ts const&amp;... args)
//        ^~~~ ---&gt; Simply &#39;auto&#39;
{
    const auto maxRest = Max(args...);
    //    ^~~~ ---&gt; Simply &#39;auto&#39;
    return (value &gt; maxRest) ? value : maxRest;
}

See live demo in godbolt.org

Alternatively, you could also use the std::common_type_t
for defining the return type.

#include &lt;type_traits&gt; // std::common_type_t

        template &lt;typename T, typename... Ts&gt;
constexpr auto Max(T const&amp; value, Ts const&amp;... args)
-&gt; std::common_type_t&lt;T, Ts...&gt;
{
    // ....
}

答案2

得分: 4

同样,我也感到我在编写更多内容以实现这个 Max 函数在 [tag:c++20] 中 [...]? 作为对其他答案的扩展,使用fold表达式Max 也可以变成非递归的。

#include <type_traits> // std::common_type, std::remove_cvref
#include <functional>  // std::greater

template<typename... T>  // 通用类型助手
using CommonType = std::common_type_t<std::remove_cvref_t<T>...>;

constexpr auto Max(auto const& value, auto const&... args)
{
    CommonType<decltype(value), decltype(args)...> maxVal = value;

    return sizeof...(args) == 0u ? maxVal 
        : (((maxVal = std::greater{}(args, maxVal) ? args : maxVal), ...)
            , maxVal);
}

在godbolt.org上查看演示


然而,对于连续的字符串文字的情况,需要一些额外的处理:

template<typename... T>  // 通用类型助手
using CommonType = std::common_type_t<std::remove_cvref_t<T>...>;

// 用于字符串文字比较。
constexpr auto handleStrLiterals(auto const& t)
{
    if constexpr (std::is_convertible_v<decltype(t), std::string_view>)
            return std::string_view{ t };
    else    return t;
};

constexpr auto Max(auto const& value, auto const&... args)
{
    CommonType<decltype(handleStrLiterals(value)), decltype(args)...>
        maxVal = handleStrLiterals(value);
    return sizeof...(args) == 0u ? maxVal 
        : (((maxVal = std::greater{}(args, maxVal) ? args : maxVal), ...)
            , maxVal);
}

在godbolt.org上查看演示

英文:

> Likewise, I am also feeling that I am writing more to achieve this Max function in [tag:c++20] [...]?

As extension to the other answer, using fold expressions, the Max can be made non-recursive as well.

#include &lt;type_traits&gt; // std::common_type, std::remove_cvref
#include &lt;functional&gt;  // std::greater

template&lt;typename... T&gt;  // common type helper
using CommonType = std::common_type_t&lt;std::remove_cvref_t&lt;T&gt;...&gt;;

constexpr auto Max(auto const&amp; value, auto const&amp;... args)
{
    CommonType&lt;decltype(value), decltype(args)...&gt; maxVal = value;

    return sizeof...(args) == 0u ? maxVal 
        : (((maxVal = std::greater{}(args, maxVal) ? args : maxVal), ...)
            , maxVal);
}

Live demo in godbolt.org


However, for the case of consecutive string literals, that requires some addition:

template&lt;typename... T&gt;  // common type helper
using CommonType = std::common_type_t&lt;std::remove_cvref_t&lt;T&gt;...&gt;;

// For string literals comparison.
constexpr auto handleStrLiterals(auto const&amp; t)
{
    if constexpr (std::is_convertible_v&lt;decltype(t), std::string_view&gt;)
            return std::string_view{ t };
    else    return t;
};

constexpr auto Max(auto const&amp; value, auto const&amp;... args)
{
    CommonType&lt;decltype(handleStrLiterals(value)), decltype(args)...&gt;
        maxVal = handleStrLiterals(value);
    return sizeof...(args) == 0u ? maxVal 
        : (((maxVal = std::greater{}(args, maxVal) ? args : maxVal), ...)
            , maxVal);
}

Live demo in godbolt.org

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

发表评论

匿名网友

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

确定