当前位置: 首页 > 知识库问答 >
问题:

是否有一种情况下,vararg函数应该优于变量模板?

颜均
2023-03-14

可变模板有很多优点,但在某些情况下,C型可变函数(使用


共有3个答案

华乐逸
2023-03-14

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

萧琛
2023-03-14

我想添加到@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更清楚。

秦新立
2023-03-14

>

如果您需要支持不支持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是合适的,并且目前正在这样做。然而,我

  • 在包的javaodoc中,在一节的末尾,我读到: 大多数流操作接受描述用户指定行为的参数,这些参数通常是lambda表达式。为了保持正确的行为,这些行为参数必须是无干扰的,并且在大多数情况下必须是无状态的。 我很难理解这个“在大多数情况下”。在哪些情况下,有状态流操作是可以接受/希望的? 还请注意,试图从行为参数访问可变状态会给您带来一个关于安全性和性能的糟糕选择;[...]最好的方法是完全避免有