如何在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,我们必须做一些工作。

  • 最后是编译器抱怨的事情:如果 blatruestd::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
        >
    

    https://godbolt.org/z/84Dcmt

  • 在 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
        >
    

    https://godbolt.org/z/2eFyX7

基于,我将模板更改为:

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。