为什么"通用参考"是否选择了重载而不是char数组或char指针?

时间:2014-07-23 07:33:56

标签: c++ templates c++11 variadic-templates typetraits

我想解压缩可变参数模板包,并根据包中的每种类型选择特定的重载。

我有3个重载来解压各种类型:

    // fixed size char arrays
    template<size_t N, typename... Ts>
    void unpack(const char (&)[N], Ts&&... ts);

    // char pointers
    template<typename... Ts>
    void unpack(const char*, Ts&&... ts);

    // all other types
    template<typename T, typename... Ts>
    void unpack(T&&, Ts&&... ts);

我将一个char数组(char buf[1024])作为左值传递给可变参数构造函数。打开包装时,正在选择T&&重载。

如何选择const char (&)[N]const char*重载?

示例应用

#include <iostream>

struct foo
{
    template<typename... Ts>
    foo(Ts&&... ts)
    {
        unpack(std::forward<Ts>(ts)...);
    }

    template<size_t N, typename... Ts>
    void unpack(const char (&)[N], Ts&&... ts)
    {
        std::cout << "const T (&)[N]" << std::endl;
        unpack(std::forward<Ts>(ts)...);
    }

    template<typename... Ts>
    void unpack(const char*, Ts&&... ts)
    {
        std::cout << "const char*" << std::endl;
        unpack(std::forward<Ts>(ts)...);
    }

    template<typename T, typename... Ts>
    void unpack(T&&, Ts&&... ts)
    {
        std::cout << "T&&" << std::endl;
        unpack(std::forward<Ts>(ts)...);
    }

    void unpack()
    {
    }
};

int main()
{
    char buf[1024];
    const char* str = "foo";
    foo f(buf, str);
    return 0;
}

以上代码会打印以下内容:

T&&
const char*

我想要打印:

const T (&)[N]
const char*

const char*
const char*

ie:选择两个char重载中的一个 - 数组或指针。

4 个答案:

答案 0 :(得分:5)

选择

void unpack(T&&, Ts&&... ts)是因为char数组中的buf不是const

如果将buf更改为char const buf[1024]{};,编译器会告诉您调用不明确,这是朝着正确方向迈出的一步。从那里你可能需要使用SFINAE(例如std::is_array),std::decay或其他一些方法来消除呼叫的歧义。

答案 1 :(得分:1)

您可以使用以下内容:

template<typename T, typename... Ts>
typename std::enable_if<!std::is_array<typename std::remove_reference<T>::type>::value>::type
unpack(T&&, Ts&&... ts)
{
    std::cout << "T&&" << std::endl;
    unpack(std::forward<Ts>(ts)...);
}

注意:目前禁止任何数组,而不仅仅是char数组。

答案 2 :(得分:1)

一种可能是删除数组重载并使用

template<typename T, typename... Ts>
std::enable_if_t<std::is_same<std::decay_t<T>, char>::value> unpack(T*, Ts&&... ts)
{
    std::cout << "(const) char*" << std::endl;
    unpack(std::forward<Ts>(ts)...);
}

仅当T被推断为(可能是cv限定的)char时才启用此功能(这意味着它与char *const char *完全匹配参数),在这些情况下,它比基本模板更专业,因此通过重载决策选择。

如果不使用C ++ 14,请将enable_if_tdecay_t替换为更详细的typename /*...*/::type

Demo

答案 3 :(得分:0)

根据您的需要,您可以检查是否与之前的重载匹配,而不是专门测试数组/指针:

int unpack_helper(...);

// fixed size char arrays
template<size_t N, typename... Ts>
void unpack_helper(const char (&)[N], Ts&&... ts);

// char pointers
template<typename... Ts>
void unpack_helper(const char*, Ts&&... ts);

// fixed size char arrays
template<size_t N, typename... Ts>
void unpack(const char (&)[N], Ts&&... ts);

// char pointers
template<typename... Ts>
void unpack(const char*, Ts&&... ts);

// all other types
template<typename T, typename... Ts, size_t = sizeof(unpack_helper(std::declval<T&&>(), std::declval<Ts&&>()...))>
void unpack(T&&, Ts&&... ts);

这有可能带来的好处,也可能是使用转换运算符选择早期函数重载的类类型的缺点。