可变函数模板中的模糊模板专业化

ambiguous template specialization in variadic function template

我正在尝试编写一个可变函数模板来计算 struct 的字节大小。这将用于我正在从事的网络编程项目。我想出的第一步是没有有效的可变参数模板:

#include <cstdint>
#include <iostream>

struct Position
{
    int32_t x;
    int32_t y;
};

template<typename T>
inline std::size_t sizeT() { return sizeof(T); }

template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
// if I change the definition of Position, I need to remember to change this function

int main(int argc, char* argv[])
{
    std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
    std::cout << "Position:" << sizeT<Position>() << std::endl;

    return 0;
}

我在我的 Ubuntu 20.04 笔记本电脑上用 g++ 9.3.0 编译并且运行良好。但是,如果我尝试将其设为可变参数模板,那么我 运行 会陷入不同的编译错误。这个可变参数模板的要点是能够像下面这样写,这样实现就不会依赖于 xy 类型的明确知识。

这是错误的代码 #1

#include <cstdint>
#include <iostream>

struct Position
{
    int32_t x;
    int32_t y;
};

template<typename T>
inline std::size_t sizeT() { return sizeof(T); }

template<>
inline std::size_t sizeT<Position>()
{
    return sizeT<decltype(Position::x), decltype(Position::y)>();
}

template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }


int main(int argc, char* argv[])
{
    std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
    std::cout << "Position:" << sizeT<Position>() << std::endl;

    return 0;
}

我得到了

error: ambiguous template specialization 'sizeT' for 'std::size_t sizeT()

note: candidate are: 'template std::size_t sizeT()'

note: template<class T, class... Ts> std::size_t sizeT()'

错误代码 #2。如果我将可变参数模板放在不同的位置:

#include <cstdint>
#include <iostream>

struct Position
{
    int32_t x;
    int32_t y;
};

template<typename T>
inline std::size_t sizeT() { return sizeof(T); }

template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }


template<>
inline std::size_t sizeT<Position>()
{
    return sizeT<decltype(Position::x), decltype(Position::y)>();
}

int main(int argc, char* argv[])
{
    std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
    std::cout << "Position:" << sizeT<Position>() << std::endl;

    return 0;
}

我得到了

error: call of overloaded 'sizeT<int8_t>()' is ambiguous

note candidate: 'std::size_t sizeT() [with T=signed char; std::size_t = long unsigned int]'

note candidate: 'std::size_t sizeT() [with T=signed char; Ts={}; std::size_t = long unsigned int]'

error: call of overloaded 'sizeT()' is ambiguous

note candidate: 'std::size_t sizeT() [with T=Position; std::size_t = long unsigned int]'

note candidate: 'std::size_t sizeT() [with T=Position; Ts={}; std::size_t = long unsigned int]'

我有点理解案例 #2,但我不理解案例 #1。我怎样才能做到这一点?提前谢谢你。

你没有使用可变参数...

你可能会这样做 (C++17)

template <typename ... Ts>
constexpr std::size_t sizeT() { return (0 + ... + sizeof(Ts)); }

template <>
constexpr std::size_t sizeT<Position>()
{
    return sizeT<decltype(Position::x), decltype(Position::y)>();
}

Demo

但是 sizeT<Position, Position> 会 return 2 * sizeof(Position) 而不是 2 * sizeT<Position>(这里是相同的)。

你可以这样做:

template <typename... Ts>
struct tag{};

template <typename T>
constexpr std::size_t sizeT(tag<T>) { return sizeof(T); }

template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts...>) { return (0 + ... + sizeT(tag<Ts>())); }

template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts>...) { return (0 + ... + sizeT(tag<Ts>())); }

constexpr std::size_t sizeT(tag<Position>)
{
    return sizeT(tag<decltype(Position::x), decltype(Position::y)>());
}

Demo