对于第一个问题:
我想写一个函数来连接字符串,它可以接收多个字符串;
#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 中,这很容易通过折叠表达式完成,但对于 C++11,我们必须做一些工作。 -
最后编译器抱怨的事情:
std::enable_if<bla>::type
计算void
是否bla
true
。这意味着你正式期待一个非类型模板参数,而编译器抱怨,因为它无法推断出它应该推断出哪个类型void
值。这通常可以通过形成指向它的指针并将其默认为nullptr
:std::enable_if<bla>::type* = nullptr
来缓解。 -
看起来 (?( 您希望
foo(someString, "stringLiteral");
工作。它不会,因为字符串文字不是std::string
。也许你想要一个不同的谓词,但对于这个答案,我会坚持原始条件。
把所有这些放在一起:
-
在 C++17 中,你会写
template <class... Args, std::enable_if_t< (std::is_same_v<std::decay_t<Args>, std::string> && ...) >* = nullptr >
https://godbolt.org/z/84Dcmt
-
在 C++11 中,我们使用此帮助程序并重新添加
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的回答,我将模板更改为:
template <class... Args,
typename std::enable_if<var_and<std::is_constructible<
std::string, Args>::value...>::value>::type * = nullptr>
在这种形式中,可以像name = foo(name, stringRed, "xxx")
一样调用函数foo
。
再次感谢@MaxLanghof。