如何在 c++11 中约束参数包类型?以及如何在 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中的所有相关方法,但没有一个有效。

对于第二个问题:

我想隐藏某个函数的实现,但是对于模板函数,它无法将定义放在.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是否blatrue。这意味着你正式期待一个非类型模板参数,而编译器抱怨,因为它无法推断出它应该推断出哪个类型void值。这通常可以通过形成指向它的指针并将其默认为nullptrstd::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。

最新更新