英文:
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 twoMax
functions into one?
答案1
得分: 13
同样,我也感到我正在更多地写代码来实现这个 Max
函数在 [tag:C++20] 中[...]?
您的 Max
函数可以通过以下方式最大化简洁性:
-
具有 abbreviated function template(自 [tag:C++20] 起)声明,以及
-
使用
if constexpr
进行编译时分支(自 [tag:C++17] 起)。
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;
}
}
更新:正如 @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;
}
}
同样,每次使用这个 Max
函数时,请始终记住检查传递的参数是否是某种指针类型,因为这不明显由它处理。
我认为错误来自函数调用:Max("string1", "string2"s));
。
我不知道如何解决这个问题。
当您调用 Max("string1", "string2"s));
时,编译器推断 T
(即返回类型)为 const char[8]
,即第一个参数的类型(即 "string1"
)。然而,第二个参数是一个 std::string
(即 "string2"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;
}
或者,您还可以使用 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
-
Having abbreviated function template (Since [tag:C++20]) declaration, and
-
The compile time branching with
if constexpr
(since [tag:C++17]).
constexpr auto Max(auto const& value, auto const&... args)
{
if constexpr (sizeof...(args) == 0u) // Single argument case!
return value;
else // For the Ts...
{
const auto max = Max(args...);
return value > max ? value : max;
}
}
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("string1"s, "string2", "string4", "string3") // result is "string2" instead of "string4"
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 <type_traits> // std::is_convertible
constexpr auto Max(auto const& value, auto const&... args)
{
if constexpr (sizeof...(args) == 0u) // Single argument case!
{
if constexpr (std::is_convertible_v<decltype(value), std::string_view>)
return std::string_view{value};
else
return value;
}
else // For the Ts...
{
const auto max = Max(args...);
return value > max ? value: max;
}
}
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("string1", "string2"s));
.
> I do not know, how can to resolve this.
When you call Max("string1", "string2"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. "string1"
). However, the second argument is a std::string
(i.e. "string2"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 <typename T, typename... Ts>
constexpr auto Max(T const& value, Ts const&... args)
// ^~~~ ---> Simply 'auto'
{
const auto maxRest = Max(args...);
// ^~~~ ---> Simply 'auto'
return (value > maxRest) ? value : maxRest;
}
Alternatively, you could also use the std::common_type_t
for defining the return type.
#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...>
{
// ....
}
答案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);
}
然而,对于连续的字符串文字的情况,需要一些额外的处理:
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);
}
英文:
> 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 <type_traits> // std::common_type, std::remove_cvref
#include <functional> // std::greater
template<typename... T> // common type helper
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);
}
However, for the case of consecutive string literals, that requires some addition:
template<typename... T> // common type helper
using CommonType = std::common_type_t<std::remove_cvref_t<T>...>;
// For string literals comparison.
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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论