可变模板有很多优点,但在某些情况下,C型可变函数(使用
vararg允许使用\uuuuuuu属性\uuuuuuu格式
。例如。
void debug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void f(float value)
{
debug("value = %d\n", value); // <- will show warning.
}
不幸的是,使用可变模板无法实现这一点。
编辑:正如Vladimir指出的,我忘了提到__attribute__格式
不是标准的一部分,但是它受到GCC和Clang(但不是Visual Studio)的支持。更多详情请见:https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
我想添加到@user2079303的优秀答案
varargs也用于某些元编程(例如,使用SFINAE实现的特性),因为它们的特性是在重载解析中被认为是最后一个。
假设我们想实现一个特性来检测一个类是否可以从某些类型(如std::is_constructible构造:
一个简化的现代实现将是这样的(这不是唯一的方法:正如所指出的,void\t
也可以用于实现trait,很快(2020?)我们不需要任何这些噱头,因为作为一等公民,require
条款中的概念正在形成):
template <class T, class... Args> struct Is_constructible {
template <class... Params>
static auto test(Params... params) -> decltype(T{params...}, std::true_type{});
static auto test(...) -> std::false_type;
static constexpr bool value = decltype(test(std::declval<Args>()...))::value;
};
这是因为SFINAE:当实例化测试时,如果语义由于某个依赖名称而无效,那么这不是一个硬错误,而只是忽略重载。
如果你想知道更多关于性状技巧、它们是如何实现的以及它们是如何工作的,你可以进一步阅读: sfinae习语、成员检测器习语、enable_if习语。
因此,对于只能从2个整数构造的类型
X
:
struct X { X(int, int) {}; };
我们得到了以下结果:
Is_constructible<X, int, int>::value // true
Is_constructible<X, int>::value; // false
现在的问题是,我们是否可以用可变模板替换varargs测试:
template <class T, class... Args> struct Is_constructible_broken {
template <class... Params>
static auto test(Params... params) -> decltype(T{params...}, std::true_type{});
template <class... Params>
static auto test(Params...) -> std::false_type;
static constexpr bool value = decltype(test(std::declval<Args>()...))::value;
};
答案是否定的(至少不是直接替代)。当我们实例化
Is_constructible_broken<X, int, int>::value
我们得到一个错误:
错误:重载“
test(int,int)
”的调用不明确
因为两个重载都是可行的,并且在重载解析中都具有相同的“等级”。使用varargs的第一个实现是有效的,因为即使两个重载都是可行的,可变模板1也比vararg优先。
事实证明,您实际上可以使用可变模板使其工作。诀窍是向
test
添加一个人工参数,它与第一个重载和第二个重载的转换完全匹配:
struct overload_low_priority{};
struct overload_high_priority : overload_low_priority {};
template <class T, class... Args> struct Is_constructible2 {
template <class... Params>
static auto test(overload_high_priority, Params... params)
-> decltype(T{params...}, std::true_type{});
template <class... Params>
static auto test(overload_low_priority, Params...) -> std::false_type;
static constexpr bool value
= decltype(test(overload_high_priority{}, std::declval<Args>()...))::value;
};
但我认为在这种情况下,varargs更清楚。
>
如果您需要支持不支持C11或更新标准的编译器,则不提供可变模板。瓦拉格斯是。
如果你需要一个编译防火墙。也就是说,您需要从标题中隐藏函数的实现,那么可变模板就不是一个选项。瓦拉格人是。
在内存受限系统(嵌入式)上,模板生成的不同函数可能会导致过多膨胀。这就是说,这样的系统通常也是实时的,在这种情况下,由于分支和堆栈的使用,varargs也可能是不可接受的。
这个问题在我的项目中经常出现。作为一个例子,假设我有两个接口,一个从API检索信息,另一个解析这些信息。 现在,我可能需要有不同的API,因此我将有许多的实现,但每个实现都需要自己的。 这看起来与Bridge设计模式所建议的非常相似,但是该模式允许任何APIClient使用任何APIParser(我说的对吗?) 那么,有没有更好的解决方案呢?或者也许这很好,不需要重构它。 另外,也许parse不是
问题内容: 我已经了解了Java 和接口之间的区别。从Java 1.5开始,已将其他功能添加到接口,并已调用这些功能以保持向后兼容性。 我的问题是,现在我们有了接口,我们应该一直使用吗?不使用和使用的用例是什么? 问题答案: 两者都有其用途,并且都由java.util.concurrent中的Executor框架支持。Runnable已经存在了更长的时间,但是它仍然在使用并且不被阻止。 可调用程序
以下代码使用gcc和MSVC编译,但使用clang失败,我使用clang-3.5和当前主干进行了测试)。 clang实例化了函数体并在上出错。gcc和MSVC只查看函数声明,而忽略正文中的。 如果您删除Constexpr,所有编译器都会很好地编译代码。 问题: 如果声明了返回类型,是否允许decltype查看函数体? 我正在寻找标准中相应章节的参考。
问题内容: 我需要检查两个地图中是否存在相同的键: 是否可以将这两个条件合而为一?我设法做这样的事情: 但我很好奇它是否可以在一种情况下完成(分配和检查)。 问题答案: 不,这是不可能的。规格:索引表达式: 地图a上的索引表达式,其类型用于特殊形式的赋值或初始化 产生另一个无类型的布尔值。的值是键是否存在于映射中,否则为false。 因此,只有在没有其他分配的情况下,才可以使用特殊形式。 但是,如
你好,我正在学习OOP,通过编写一个虚拟的库管理项目在Java。 在serachBook()中,如果在ArrayList中找到book,则返回book对象,如果未找到,则抛出自定义异常BookNotFound。 问题1:它应该只返回null并在调用代码时检查返回值是否为null,还是抛出自定义异常BookNotFound。 目前我认为BookNotFinder是合适的,并且目前正在这样做。然而,我
我正在训练一个复杂的神经网络架构,我使用RNN对输入进行编码,然后是一个具有softmax输出层的深层神经网络。 我现在正在优化我的体系结构深层神经网络部分(单元数和隐藏层数)。 我目前正在为所有层使用sigmoid激活。对于少数隐藏层来说,这似乎是可以的,但随着层数的增加,似乎sigmoid不是最佳选择。 你认为我应该先对sigmoid进行超参数优化,然后再对ReLu进行优化,还是直接使用ReL