C++:具有可变参数模板的成员函数指针参数

标签 c++ templates c++11 variadic-templates pointer-to-member

我正在尝试为我正在进行的项目编写一个简短的单元测试程序。

如果给定函数抛出某种异常,则此测试通过:

template <class Exception, class Return, class... Args>
bool throws(std::string s, Return(*fn)(Args...), Args... args)
{
  try
  {
    fn(args...);
  }
  catch (Exception& e)
  {
    return output(s, true);
  }
  catch (...)
  {
    return output(s, false);
  }
  return output(s, false);
}

使用它看起来像这样:

throws<int>("Testing: Throws int", &foo);

而且效果很好。但现在我正在尝试编写一个类似的函数,该函数将与类的成员函数一起使用。

这是我到目前为止所得到的:

template <class Exception, class Object, class Return, class... Args>
bool throws(std::string s, Object& o, Return(Object::*fn)(Args...), Args... args)
{
  // ...
  (o.*fn)(args...);
  // ...
}

template <class Exception, class Object, class Return, class... Args>
bool throws(std::string s, const Object& o, Return(Object::*fn)(Args...) const, Args... args)
{ /* ... */ }

使用时应该看起来像这样:

Bar<int> b1(5), b2(2);
typedef Bar<int>& (Bar<int>::*fn)(const Bar<int>&);
typedef Bar<int> (Bar<int>::*const_fn)(const Bar<int>&) const;

fn foo = &Bar<int>::foo;
const_fn const_foo = &Bar<int>::const_foo;

throws<int>("Testing member function", b1, foo, b2);
throws<int>("Testing const member function", b1, const_foo, b2);

但是当我这样做时,对于这两个 throws() 函数,我都得到“没有匹配的函数调用”。具体来说:

error: no matching function for call to ‘throws(const char [30], Bar<int>&, Bar<int> (Bar<int>::*&)(const Bar<int>&)const, const Bar<int>&)

对于非常量版本也是类似的。

我注意到一些不同的 const,所以我尝试在我使用该函数的地方加入一些 const_cast,但没有骰子。这是我第一次使用成员函数指针和可变参数模板(根本没有,更不用说同时使用了),所以我肯定会错过一些东西……比我更有经验的人有什么想法吗?

我唯一无法解释的是 (Bar<int>::*&) .最后的 & 与调用不匹配...但这应该不是问题,不是吗?

编辑: 根据要求,我的 Bar 类:

template <class T>
class Bar
{
private:
  T _data;
public:
  Bar (const int i) : _data(i) {}
  Bar const_foo (const Bar& other) const
  {
    if (_data != other._data)
    {
      throw _data;
    }
    return Bar(_data);
  }
  Bar& foo (const Bar& other)
  {
    if (_data != other._data)
    {
      throw _data;
    }
    return *this;
  }
};

以及完整的错误:

test.cpp: In function ‘int main()’:
test.cpp:111:53: error: no matching function for call to ‘throws(const char [24], Bar<int>&, Bar<int>& (Bar<int>::*&)(const Bar<int>&), Bar<int>&)’
test.cpp:111:53: note: candidates are:
test.cpp:31:6: note: template<class Exception, class Return, class ... Args> bool throws(std::string, Return (*)(Args ...), Args ...)
test.cpp:53:6: note: template<class Exception, class Object, class Return, class ... Args> bool throws(std::string, Object&, Return (Object::*)(Args ...), Args ...)
test.cpp:75:6: note: template<class Exception, class Object, class Return, class ... Args> bool throws(std::string, const Object&, Return (Object::*)(Args ...)const, Args ...)
test.cpp:112:65: error: no matching function for call to ‘throws(const char [30], Bar<int>&, Bar<int> (Bar<int>::*&)(const Bar<int>&)const, Bar<int>&)’
test.cpp:112:65: note: candidates are:
test.cpp:31:6: note: template<class Exception, class Return, class ... Args> bool throws(std::string, Return (*)(Args ...), Args ...)
test.cpp:53:6: note: template<class Exception, class Object, class Return, class ... Args> bool throws(std::string, Object&, Return (Object::*)(Args ...), Args ...)
test.cpp:75:6: note: template<class Exception, class Object, class Return, class ... Args> bool throws(std::string, const Object&, Return (Object::*)(Args ...)const, Args ...)

最佳答案

我注意到您使用的非成员版本带有一个参数为零的函数。我相信当您尝试传递参数时您的代码会失败,而不是当您将其更改为采用成员函数时。通过将其更改为采用成员函数并在同一步骤中传递参数,您混淆了失败的真正原因。我相信如果您尝试使用零参数的成员函数,您的代码会起作用。而且我相信,如果您尝试将它与带有一个或多个参数的函数一起使用,您的非成员版本将会失败。

template <class Exception, class Object, class Return, class... Args>
bool throws(std::string s, Object& o, Return(Object::*fn)(Args...), Args... args)

这里您告诉编译器 throws 的第四个参数的类型并且函数指针的第一个参数必须完全相同

error: no matching function for call to ‘throws(const char [24], Bar<int>&,
       Bar<int>& (Bar<int>::*&)(const Bar<int>&), Bar<int>&)’

编译器告诉你 throws 的第四个参数和函数指针的第一个参数不同,所以找不到 throws 的声明哪个匹配。您的模板无法匹配,因为这意味着 Args...必须推断为 const Bar<int>&Bar<int>&同时,这是两种不同的类型。

@WhozCraig 在评论中提供的解决方案有效,因为函数类型和参数类型是独立推导的。他的解决方案通过通用引用传递函数指针,但这是偶然的;它也可以通过 const 获取函数指针左值引用,或按值。

template <class Exception, class Func, class... Args>
bool throws(std::string s, Func fn, Args&&... args)
{ /*...*/ }

template <class Exception, class Object, class MemFunc, class... Args>
bool throws(std::string s, Object& o, MemFunc fn, Args&&... args)
{ /*...*/ }

不幸的是,使用这种方法您可能会遇到过载不明确的情况。解决这个问题的简单方法是重命名成员版本 memberThrows .更复杂的版本是使用标签分派(dispatch)或 SFINAE(在这种情况下我更喜欢前者)。

template <class Exception, class Func, class... Args>
bool throwsHelper(std::false_type, std::string s, Func fn, Args&&... args)
{ /*...*/ }

template <class Exception, class MemFunc, class Object, class... Args>
bool throwsHelper(std::true_type, std::string s, MemFunc fn, Object&& o, Args&&... args)
{ /*...*/ }

template <class Exception, class Func, class... Args>
bool throws(std::string s, Func fn, Args&&... args)
{
    using isMember = typename std::is_member_pointer<Func>::type;
    return throwsHelper(isMember(), s, fn, std::forward<Args>(args)...);
}

请注意,我必须将参数的顺序更改为成员版本,以允许在两种情况下统一调用。

关于C++:具有可变参数模板的成员函数指针参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23399050/

相关文章:

c++ - 如何使用 C/C++ 无需驱动程序直接从 USB 端口访问 USB 条码扫描仪数据

c++ - 在模板类中调用模板方法

php - 是否可以在 Silverstripe 模板变量上运行一个函数来格式化输出?

c++ - 重复调用PyRun_SimpleFile出现Segmentation fault

c++ - 如果我将 glUniform1f 调用放在渲染循环之前,则不会存储统一值

抽象类的 C++ 保护构造函数

python - 在模板中使用 Django View 变量

c++ - 如何以 C++11 样式初始化数组?

c++ - 当作为 Function 的参数传递给线程时,为什么仿函数的 dtor 调用两次(多次)?

c++ - 使用 g++8 和 c++20 的 std::async 的编译问题