可变参数模板

Posted

技术标签:

【中文标题】可变参数模板【英文标题】:Variadic templates 【发布时间】:2010-09-21 12:42:47 【问题描述】:

C++0x 将允许模板采用任意数量的参数。除了实现元组之外,此功能的最佳用途是什么?

【问题讨论】:

【参考方案1】:
    类型安全的 printf工厂方法中转发任意多个构造函数参数 拥有任意基类允许放置和删除有用的策略。 通过使用可变参数模板构造函数将异构类型对象直接移动到容器中进行初始化。 拥有一个文字运算符,可以为用户定义的文字(如“10110b”)计算值。

样本到 3:

template<typename... T> struct flexible : T...  flexible(): T()...   ;

样本到 4:

struct my_container  template<typename... T> my_container(T&&... t)   ;
my_container c =  a, b, c ;

样本到 5:

template<char... digits>
int operator "" b()  return convert<digits...>::value; 

查看此示例代码:here

【讨论】:

【参考方案2】:

也许你会对 Andrei Alexandrescu 关于 Going Native 2012 活动的演讲感兴趣:

Here 是视频,Here 是文档。

【讨论】:

【参考方案3】: 类型安全printf

【讨论】:

是的,但我仍然需要看到一个非常优雅的实现:-) 我认为这个具体的例子有点退化。请记住,对于每个实例化,编译器都需要创建更多代码。你能想象为使用的每个可能的参数组合创建 100 多个版本的 printf 吗?哦,臃肿! @Shy:如果所有版本都是 real printf 的内联包装器,则不会。 @shoosh:“main”函数可以是展开为(内联)顺序操作的循环,每个操作处理一个(模板化)函数参数。【参考方案4】:

允许像 Boost.Function 这样的东西采用任意数量的参数

【讨论】:

【参考方案5】:

我刚刚写了一封article,介绍了如何使用 C++0x 可变参数模板实现多个 COM 接口并保持代码紧凑和优雅。

【讨论】:

【参考方案6】:

我已经实现了一个 NDArray(N 维数组),它具有方法 setSizes 和可变参数计数。使用可变参数模板参数比使用可变参数函数参数类型更安全,而且我可以仅使用可变参数模板参数在编译时控制传递给此函数的参数计数。

void setSizes(uintmax_t currentSize) 
    static_assert(1 == NDimensions, "Invalid count of arguments given to setSizes.");

    size_ = currentSize;
    data_ = new NDArrayReferenceType[currentSize];


template <typename... Sizes>
void setSizes(uintmax_t currentSize, Sizes... sizes) 
    static_assert(sizeof...(Sizes) + 1 == NDimensions, "Invalid count of arguments given to setSizes.");

    size_ = currentSize;
    data_ = new NDArrayReferenceType[currentSize];

    for (uintmax_t i = 0; i < currentSize; i++) 
        data_[i]->setSizes(sizes...);
    

我还为我自制的 SmartPointer 实现了一个通用的构造函数包装器。它包装了所有用户定义的原始指针类型的构造函数。

template <typename TSmartPointer, typename... Args>
static inline void initialize(TSmartPointer *smartPointer, Args... args) 
    smartPointer->pointer_ = new typename TSmartPointer::PointerType(std::forward<Args>(args)...);
    smartPointer->__retain();

这段代码似乎不明显,这是SmartPointer初始化程序的一部分,用于SmartPointer是否应该在获取SmartPointer(RAII)时自动调用指针的构造函数。如果是抽象类,则无法调用构造函数。

所以,如果我有一个 AbstractObject 类型,它是一个抽象类的 SmartPointer,以及一个 ConcreteObject 类型,它是具有两个 int 构造函数的类的 SmartPointer,我可以编写以下代码:

AbstractObject object = ConcreteObject(42, 42);

它类似于 C# 和 Java(但带有 RAII),它适用于 C++/GCC 4.8 =)

【讨论】:

【参考方案7】:

使用动态参数编号为每个调用键入安全性。

【讨论】:

【参考方案8】:

类型安全的 printf 已在其他答案中提到,但更普遍的可变参数模板可用于实现完全不需要通过格式说明符传递类型信息的格式化函数。比如C++ Format library实现的格式化函数类似于Python的str.format:

fmt::print("I'd rather be 1 than 0.", "right", "happy");

除了安全的 printf。在 C++11 中使用可变参数模板自动捕获参数的类型。

这使得像 lld 或臭名昭著的 PRIdPTR 这样的 printf 说明符变得不必要了,而不是

std::printf("Local number: %" PRIdPTR "\n\n", someIntPtr);

一个人可以简单地使用

fmt::printf("Local number: %d\n\n", someIntPtr);

免责声明:我是这个库的作者

【讨论】:

以上是关于可变参数模板的主要内容,如果未能解决你的问题,请参考以下文章

可变参数模板类:是不是可以为每个可变参数模板参数实现一个唯一的成员函数?

为啥可变参数模板的模板特化与非可变模板的特化不同?

C++11 ——— 可变参数模板

C++11 ——— 可变参数模板

C/C++中的可变参数和可变参数模板

[C++11 模板的改进] --- 可变参数模板