如何在c++11中约束参数包类型?以及如何在cpp中实现模板?
How to constraint the parameter package type in c++11? And How to implement the template in cpp?
第一个问题:
我想写一个函数来连接字符串,它可以接收多个字符串;
#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
中的所有相关方法,但其中none个有效。
第二个问题:
我想隐藏一些函数的实现,但是对于模板函数,不能把定义放在.hpp
,把实现放在.cpp
,编译器会抛出 undefined reference
错误。有什么优雅的方法可以解决这个问题吗?
谢谢。
这里有一点需要展开。
std::decay<Args...>::type
不行。 std::decay
只接受一个模板参数,但您试图在此处扩展包。扩展需要发生在 is_same
.
您还缺少一种聚合所有 is_same
谓词的方法。您是要 and
全部还是 or
全部?大概是and
。在 C++17 中,使用折叠表达式很容易完成,但对于 C++11,我们必须做一些工作。
最后是编译器抱怨的事情:如果 bla
是 true
,std::enable_if<bla>::type
的计算结果是 void
。这意味着您正式期待一个非类型模板参数,并且编译器会抱怨,因为它无法推断出它应该推断出哪个 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
>
在 C++11 中,我们使用 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
>
基于,我将模板更改为:
template <class... Args,
typename std::enable_if<var_and<std::is_constructible<
std::string, Args>::value...>::value>::type * = nullptr>
在这种形式下,函数foo
可以像name = foo(name, stringRed, "xxx")
一样被调用。
再次感谢@MaxLanghof。
第一个问题:
我想写一个函数来连接字符串,它可以接收多个字符串;
#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
中的所有相关方法,但其中none个有效。
第二个问题:
我想隐藏一些函数的实现,但是对于模板函数,不能把定义放在.hpp
,把实现放在.cpp
,编译器会抛出 undefined reference
错误。有什么优雅的方法可以解决这个问题吗?
谢谢。
这里有一点需要展开。
std::decay<Args...>::type
不行。std::decay
只接受一个模板参数,但您试图在此处扩展包。扩展需要发生在is_same
.您还缺少一种聚合所有
is_same
谓词的方法。您是要and
全部还是or
全部?大概是and
。在 C++17 中,使用折叠表达式很容易完成,但对于 C++11,我们必须做一些工作。最后是编译器抱怨的事情:如果
bla
是true
,std::enable_if<bla>::type
的计算结果是void
。这意味着您正式期待一个非类型模板参数,并且编译器会抱怨,因为它无法推断出它应该推断出哪个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 >
在 C++11 中,我们使用 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 >
基于
template <class... Args,
typename std::enable_if<var_and<std::is_constructible<
std::string, Args>::value...>::value>::type * = nullptr>
在这种形式下,函数foo
可以像name = foo(name, stringRed, "xxx")
一样被调用。
再次感谢@MaxLanghof。