【问题标题】:Mixing const and non-const variables on variadic template在可变参数模板上混合 const 和非 const 变量
【发布时间】:2019-07-18 19:47:40
【问题描述】:

我开发了几个通用的读取器和写入器包装函数(主要是为了避免在读取或写入文件的代码的每个部分都必须处理异常)。我的函数有这些签名:

写作:

/** Generic writer function alias for free functions */
template<typename ... Args>
using WriterFunction = void (*)(std::ofstream &, Args const & ... args);
/**
 * \brief Wrapper used to encapsulate a generic free function writing to an output stream
 * \param[in] outFileName Name of the output file to be written.
 * \param[in] writeFunction Function used to export the data.
 * \param[in] ... Variable number of arguments to be passed to the previous function.
 */
template<typename ... Args>
void writerFunctionWrapper(
    std::string const &outFileName,
    WriterFunction<Args ...> writeFunction,
    Args const & ... args);

阅读:

/** Generic reader function alias for free functions */
template<typename ... Args>
using ReaderFunction = void (*)(std::ifstream &, Args & ... args);
/**
 * \brief Wrapper used to encapsulate a generic free function reading from an input stream
 * \param[in] inFileName Name of the input file to be read.
 * \param[in] readFunction Function used to import the data.
 * \param[in] ... Variable number of arguments to be passed to the previous function.
 */
template<typename ... Args>
void readerFunctionWrapper(
    std::string const &inFileName,
    ReaderFunction<Args ...> readFunction,
    Args & ... args);

因此,主要区别在于作者将输入参数视为const,因为我希望将它们读入std::ostream,反之亦然,读者将输入参数视为non-const,因为我希望通过std::istream 给他们写信。

这很好用,但我想在变量列表中包含带有 const 参数的读取器函数,或者带有 non-const 参数的写入器函数。但是,当参数列表是可变的时,我不知道该怎么做。

我尝试将我的别名和模板修改为

template<typename ... ArgsConst, typename ... ArgsNonConst>
using WriterFunction = void (*)(std::ofstream &, ArgsConst const & ... argsConst, ArgsNonConst & ... argsNonConst);
template<
    typename ... ArgsConst,
    typename ... ArgsNonConst>
void writerFunctionWrapper(
    std::string const &outFileName,
    WriterFunction<ArgsConst ..., ArgsNonConst ...> writeFunction,
    ArgsConst const & ... argsConst,
    ArgsNonConst & ... argsNonConst);

它的缺点是强制将参数放置在writeFunction() 中,首先是const,然后是非const,但这是一个较小的邪恶。但是,这对我不起作用:

/home/code/Utils/include/file_utils.h:42:11: error: parameter pack ‘ArgsConst’ must be at the end of the template parameter list
  template<typename ... ArgsConst, typename ... ArgsNonConst>

我还尝试通过将别名和模板扩展为:

/** Generic writer function alias for free functions */
template<typename ... Args>
using WriterFunctionGeneral = void (*)(std::ofstream &, Args const & ... argsConst, Args & ... argsNonConst);
template<typename ... Args>
void writerFunctionWrapper(
    std::string const &outFileName,
    WriterFunctionGeneral<Args ...> writeFunctionGeneral,
    Args const & ... argsConst,
    Args & ... argsNonConst);

但是当我尝试在混合const 和非const 参数的函数上使用它时,这也不起作用,因为:

void writeSomeEntity(
    std::ofstream &outWriter,
    ConstOutVar const &constVar,
    NonConstOutVar &nonConstVar);

并将其调用为:

ConstOutVar constVar;
NonConstOutVar nonConstVar;
...
Utils::writerFunctionWrapper(outFileName, &writeSomeEntity, constVar, nonConstVar);

但这也没有用:

/home/code/OutputWriter/src/entity_writer.cpp:46:122: error: no matching function for call to ‘writerFunctionWrapper(std::string&, void (*)(std::ofstream&, const ConstOutVar &, NonConstOutVar &), ConstOutVar &, NonConstOutVar &)’
  Utils::writerFunctionWrapper(outFileName, writeSomeEntity, constOutVar, nonConstOutVar);

(注意错误如何显示 constOutVar & 而不是 constOutVar const &

我注意到 [this question] (Variadic template trouble matching const and non-const std::string),但这并不是我在这里描述的情况。

有什么建议吗?

非常感谢。

【问题讨论】:

  • 这很好用,但我已经到了一个地步,我希望在其变量列表中拥有带有 const 参数的读取器函数,或者带有非 const 参数的写入器函数. 为什么?您不能在阅读器中修改 const 对象,也不应该修改正在编写的对象。
  • 你要找的是forwarding references
  • @NathanOliver,为什么不呢?例如,我可以使用一些参数来返回我写入的字节数。甚至可以将此示例视为更通用的示例,而不仅仅是与读者或作者相关联,假设您要传递一组可变变量,一些是 const,一些是非 const。
  • @A.Palma 首先,您应该避免使用 out 参数。如果您需要从函数返回数据,这就是返回类型的用途。其次,您可以将非常量对象传递给采用const&amp; 的函数。在 write 函数中将参数设置为 const 可以防止您犯意外错误。

标签: c++ variadic-templates template-argument-deduction


【解决方案1】:

有什么建议吗?

只有三个。

(1) 接受包装器中的函数作为通用模板类型,忽略接收到的参数的确切类型;这样,您的包装器将更加灵活,因为还可以接受 std::function 或 lambda(也是通用 lambda)或(几乎相同)带有 operator() 的结构/类(可能是模板)。这是不可能的,你只接受函数指针

(2) 接收参数作为通用引用 转发引用并使用完美转发;完美转发正是为解决您面临的问题而设计的。

按照建议 (1) 和 (2),您需要一个包装器(如果它们都只接受输入和输入/输出参数,为什么还需要一个读取器和一个写入器包装器?),以下可能是简化的例子

template <typename F, typename ... Args>
void FunctionWrapper(F && f, Args && ... args)
 { std::forward<F>(f)(std::forward<Args>(args)...); }

但是,如果您真的(真的!)想要一个或多个仅接受传统函数指针的包装器,建议 (3)

(3) 使用两组可变参数模板类型作为参数;一组用于函数指针,一组用于参数;我的意思是......如下所示

template <typename ... FAs, typename ... As>
void FunctionWrapperPnt (void(*f)(FAs...), As && ... as)
 { f(std::forward<As>(as)...); }

这很重要,以避免您可以传递一个函数,该函数接收(通过示例)std::string 和一个作为char const [](字符串文字)的参数,在推导类型时会出错。

【讨论】:

  • @NathanOliver - 我曾经混淆这两个术语;更正;谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-28
  • 1970-01-01
  • 2012-05-02
  • 2016-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多