我想编写一个连接字符串的函数,它可以接收多个字符串;
#include <string>
#include <vector>
#include <type_traits>
template <class... Args, typename std::enable_if<std::is_same<typename std::decay<Args...>::type, std::string>::type>::type>
std::string foo(const std::string &first, const Args &... senconds) {
std::string delimiter = "$$";
std::string ret = first;
std::vector<std::string> vec{senconds...};
for (auto second = vec.rbegin(); second != vec.rend(); second++) {
ret = delimiter + *second + delimiter + ret;
}
return ret;
}
但是当我像这样调用它时:
std::string name = "x";
name = foo(name, "xxx");
编译器将抛出错误:
error: no matching function for call to ‘foo(std::__cxx11::string&, const char [4])’
并且会有一些注释:
note: couldn't deduce template parameter ‘<anonymous>’
[我认为我应该修改模板中的约束,并且我已经尝试了type_traits
中的所有相关方法,但是它们都不起作用。
我想隐藏某些函数的实现,但是对于模板函数,它无法将定义放入.hpp
中,而无法将实现放入.cpp
中,编译器将抛出undefined reference
错误。有什么优雅的方法可以解决这个问题吗?
谢谢。
这里有些东西要解开。
std::decay<Args...>::type
无法工作。 std::decay
仅接受单个模板参数,但是您尝试在此处扩展包。扩展需要在is_same
上进行。
您还缺少一种汇总所有is_same
谓词的方法。您要全部and
还是全部or
?大概是and
。在C ++ 17中,使用fold表达式可以轻松完成此操作,但是对于C ++ 11,我们必须花点功夫。
最后,编译器会抱怨:如果std::enable_if<bla>::type
为void
,则bla
的值为true
。这意味着您正式希望使用非类型的模板参数,并且编译器会抱怨,因为它无法推断应推导哪种类型的void
值。通常可以通过形成指向它的指针并将其默认设置为nullptr
:std::enable_if<bla>::type* = nullptr
来缓解此问题。
您似乎期望(C0)起作用(?)。不会,因为字符串文字不是foo(someString, "stringLiteral");
。也许您想要一个不同的谓词,但是对于这个答案,我将坚持原始条件。
将所有内容放在一起:
在C ++ 17中,您会写
std::string
template <class... Args,
std::enable_if_t<
(std::is_same_v<std::decay_t<Args>, std::string> && ...)
>* = nullptr
>
在C ++ 11中,我们使用https://godbolt.org/z/84Dcmt帮助程序,并添加this和typename
详细程度:
::type
template <class... Args,
typename std::enable_if<
var_and<
std::is_same<typename std::decay<Args>::type, std::string>::value...
>::value
>::type* = nullptr
>
基于https://godbolt.org/z/2eFyX7,我将模板更改为:
MaxLanghof's answer
以这种形式,可以像template <class... Args,
typename std::enable_if<var_and<std::is_constructible<
std::string, Args>::value...>::value>::type * = nullptr>
一样调用功能foo
。
再次感谢@MaxLanghof。