以下可变参数模板行为是否不一致?
Is the following Variadic template behavior inconsistent?
我正在尝试以下示例来理解可变参数模板,发现行为有些不一致。
#include <iostream>
#include <string>
using namespace std;
template<typename T>
T adder(T v) {
return v;
}
template<typename T, typename... Args>
T adder(T first, Args... args) {
return first + adder(args...);
}
int main()
{
long sum = adder(1, 2, 3, 8, 7); //Works
cout << sum << endl;
string s1 = "xx", s2 = "aa", s3 = "bb", s4 = "yy";
string ssum = adder(s1, s2, s3, s4); //Works
cout << ssum << endl;
string ssum2 = s1 + s2 + "3" + "4"; //Works as operator '+' is defined in string class for const char*
cout << ssum2 << endl;
string ssum3 = adder(s1, s2, "3", "4"); //Does not work. Expected as binary 'operator+' is not defined for string and const char*
cout << ssum3 << endl;
string ssum4 = adder("3", "4"); //Does not work. Expected as binary 'operator+' is not defined for const char* and const char*
cout << ssum4 << endl;
string ssum5 = adder(s1, s2, "3"); //Works!!!
cout << ssum5 << endl;
}
为ssum3
和ssum4
调用adder
失败,但适用于ssum5
。这种行为是否一致?发生这种情况是因为ssum5
的最后一个参数在最后一次迭代中转换为string
吗?
这是因为
ssum5
的最后一个参数在最终迭代时转换为string
吗?
不,它之所以有效std::operator+(std::basic_string)
是因为被重载以接受std::string
和原始字符串 (const char*
(。给定s2
是一个std::string
,那么s2 + "3"
和"3" + s2
都可以正常工作。所以ssum5
工作,因为最后递归它会被解释为s2 + "3"
,这很好。
问题是您无法将两个连续的原始字符串传递给adder
。对于ssum3
和ssum4
你传递"3"
和"4"
,最后它们将被解释为"3" + "4"
,这显然不起作用。
您可以使用std::common_type
修复它。
正如其他人所指出的,这与递归的顺序有关。
adder(s1, s2, "3")
与以下相同:
s1 + adder(s2, "3")
这是有效的,因为这与(std::strings
求和,并且还用const char*
对std::string
求和,这是合法的(:
s1 + (s2 + ("3")))
另一方面
adder(s1, s2, "3", "4");
显然不能工作,因为它与(最终添加两个const char*
相同,这是非法的(:
s1 + (s2 + ("3" + ("4")))
要克服这个问题,您应该使用std::common_type
,它将执行通用类型中的所有添加(在这种情况下std::string
(:
template<typename T>
T adder(const T & v) {
return v;
}
template<typename T, typename... Args>
T adder(const T & first, const Args &... args) {
return first + adder<typename std::common_type<T, Args...>::type>(args...);
}
另一种选择是使用 C++17 折表达式(如果您有 C++17(:
template <typename ... Args>
typename std::common_type<Args...>::type
adder2(const Args & ... args)
{
using type = typename std::common_type<Args...>::type;
return (type(args) + ... );
}
缺点是它会导致字符串等类型的复制构造函数,并且需要 C++17。通过使用帮助程序函数,可以摆脱额外的构造(包括公共基类(:
template <typename Target, typename Source>
typename std::enable_if< ! std::is_base_of<Target, Source>::value,
Target>::type
toType(const Source & source)
{
return source;
}
template <typename Target, typename Source>
typename std::enable_if<std::is_base_of<Target, Source>::value,
const Target&>::type
toType(const Source & source)
{
return source;
}
template <typename ... Args>
typename std::common_type<Args...>::type
addder3(const Args & ... args)
{
using type = typename std::common_type<Args...>::type;
return (toType<type>(args) + ... );
}
ssum3 是adder(s1, adder(s2, adder("3", adder("4"))))
ssum5 是adder(s1, adder(s2, adder("3")))
第一种情况包含即时adder("3", adder("4"))
,它最终扩展到"3" + "4"
,这显然不起作用。
第二种情况永远不会尝试将两个const char*
加在一起,所以没关系。
- 检查输入是否不是整数或数字
- 大于65535的C++数组[size]引发不一致的溢出
- 在 C++(和 C)中进行类型转换时明显不一致
- 填充上编译器生成的复制构造函数之间的不一致
- glibcxx STL 在实现 std::valarray::sum() 时是否不正确?
- 犰狳的 print() 方法和 cout 在从 Rcpp 调用时顺序不一致
- 我是否不正确地集中了这些字符数组?
- CreateDIBSection为同一图像返回不一致的位图位值
- 在 Qml 中从 QSqlTableModel 中删除单行时视图不一致
- 模板参数推导不一致
- 声明中不一致的no是否违反ODR?
- 如何删除分支因子不一致的树,最大为 30,40
- 从 C++ 函数与 Python 函数返回的不一致值用于偏斜正态分布
- 从 C 字符串构造 std::string 与从另一个 std::string 构造 std::string 不一致
- 这种比较是否不一致(或者存在其他问题)?
- 以下可变参数模板行为是否不一致?
- 如何修复我的链表读数不一致的问题?
- 我的随机生成器是否不工作,或者我决定人/骨架是否击中对手的方式是否有错误
- 这种编译器优化不一致是否完全由未定义的行为来解释
- 类中定义的友元函数模板是否可用于查找?clang++和g++不一致