可以使用以下方法“剥离”函数参数类型:
void foo_double(double a)
{
}
void foo_int(int a)
{
}
template <class R, class A0>
void bar(R (*fp)(A0))
{
// Do something related with A0
}
int main()
{
bar(foo_double); // A0 is double
bar(foo_int); // A0 is int
}
是否可以对类构造函数执行相同的“参数类型剥离”?编辑:
我相信我没有在原始代码片段中清楚地解释自己。这是完整的方案。
我有多个类
C1,...,Cn
,需要将它们作为函数公开给python。假设所有类都有一个共同的void Run()
方法。但是,这些类的构造函数接受不同的参数。要将函数公开给python,我使用boost.python,它会在处理所有类型转换(主要是基元)时自动将函数导出到适当的python函数。我的第一个解决方案是:
class C1
{
public:
C1() {}
void Run();
};
class C2
{
public:
C2(double a) {}
void Run();
};
template <class T>
void python_wrapper()
{
T instance();
instance.Run();
}
template <class T, class A0>
void python_wrapper(A0 a0)
{
T instance(a0);
instance.Run();
}
BOOST_PYTHON_MODULE(_pythonmodule)
{
// This is boost.python stuff
python::def("f1", python_wrapper<C1>);
python::def("f2", python_wrapper<C2, double>);
}
而且...有效。我现在要完成的工作是在推断构造函数参数类型时使用
python_wrapper<C2>
而不是python_wrapper<C2, double>
。正如我在原始帖子中所示。如果我包装函数而不是类,则可以完成类似的操作。
最佳答案
无法推断类型的构造函数的参数。
C++ 98/03和C++ 11规范明确列出了可能发生类型推导的上下文(请参见第14.8.2节及其子节)。演绎可以提高模板编程的寿命,不是强制性的。可以通过演绎完成的所有操作也可以通过显式调用来实现。因此,为了使推演成为可能,将要求人们可以明确地向函数模板提供构造函数。
但是,这是不可能的。如C++ 98/03和C++ 11规范的第12.1节所述,构造函数没有名称。此外,C++ 98/03的12.1.12节和C++ 11的12.1.10节规定,不得使用构造函数的地址。因此,无法提供标识符。因此,不可能发生扣除。
由于不可能进行扣除,因此可能值得考虑其他解决方案。每个解决方案都有自己的优缺点集,但是所有这些解决方案都需要在构造函数之外的某些上下文中明确列出参数类型:
考虑到针对您的环境描述的情况:
考虑到C++规范,我相信我们已经定义了Kobayashi Maru。您将需要权衡利弊,以确定哪种方法可以适合您的环境。最简单的方法可能已经是您已经使用的方法,因为随着创建更多类型,它只需要一个位置即可更改代码。
不过,这是一种使用类型特征的方法,该方法以非侵入方式提供有关类型构造函数的信息。如果没有C++ 11功能(如可变参数模板),则会有一些样板代码。此外,该实现可能无法涵盖所有情况,例如多个构造函数。
使用原始问题中显示的类:
class C1
{
public:
C1();
void Run();
};
class C2
{
public:
C2(double a);
void Run();
};
将使用代表构造函数特征的模板。我正在使用Boost.MPL提供的类型列表来表示构造函数的参数类型。默认的
constructor_traits
指示不需要任何参数。/// @brief constructor_traits is a type_trait that is used to noninvasively
/// associated T with constructor meta information, such as T'
/// constructor's argument types.
///
/// For example, if Foo has a constructor that accepts a single
/// integer, then constructor_traits<Foo>::type should be
/// boost::mpl::vector<int>.
template <typename T>
struct constructor_traits
{
typedef boost::mpl::vector<> type;
};
然后,此特征专门用于带有接受参数的构造函数的类型,例如
C2
。/// Specialize constructor_traits for C2 to indicate that its constructor
/// accepts a double.
template <>
struct constructor_traits<C2>
{
typedef boost::mpl::vector<double> type;
};
boost::mpl::vector
是代表构造函数参数的类型列表。它通过 boost::mpl::at
提供随机访问。为了提供对元素的更简洁的访问,引入了一个辅助类型:/// @brief Helper type that makes getting the constructor argument slightly
/// easier.
template <typename Vector,
std::size_t Index>
struct get_constructor_arg
: boost::mpl::at<Vector, boost::mpl::int_<Index> >
{};
在将函数公开给Boost.Python时,所需的语法仅提供一种类型。功能模板或类模板均可用于解决此问题。我决定使用类模板,因为它减少了一些样板代码。
/// @brief runner type is used to provide a static run function that
/// will delegate the construction and running of type T based
/// on T's constructor_traits.
template <typename T,
typename Args = typename constructor_traits<T>::type,
std::size_t = boost::mpl::size<Args>::value>
struct runner
{
static void run()
{
T().Run();
}
};
然后,此模板专门用于构造函数接受的参数数量。下面专门接受一个参数。这由特化模板参数列表中的
1
确定。/// Specialization for runner for types with have a single argument
/// constructor.
template <typename T,
typename Args>
struct runner<T, Args, 1>
{
static void run(typename get_constructor_arg<Args, 0>::type a0)
{
T(a0).Run();
}
};
功能模板也可以用于解决此问题。我决定使用类模板是因为:
enable_if
构造来选择正确的模板。 constructor_trait
的需要。 生成的Boost.Python调用看起来像:
BOOST_PYTHON_MODULE(_pythonmodule)
{
boost::python::def("f1", &runner<C1>::run);
boost::python::def("f2", &runner<C2>::run);
}
这是完整的代码:
#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/python.hpp>
class C1
{
public:
C1() {}
void Run() { std::cout << "run c1" << std::endl; }
};
class C2
{
public:
C2(double a) : a_(a) {}
void Run() { std::cout << "run c2: " << a_ << std::endl;}
private:
double a_;
};
/// @brief constructor_traits is a type_trait that is used to noninvasively
/// associated T with constructor meta information, such as T'
/// constructor's argument types.
///
/// For example, if Foo has a constructor that accepts a single
/// integer, then constructor_traits<Foo>::type should be
/// boost::mpl::vector<int>.
template <typename T>
struct constructor_traits
{
typedef boost::mpl::vector<> type;
};
/// Specialize constructor_traits for C2 to indicate that its constructor
/// accepts a double.
template <>
struct constructor_traits<C2>
{
typedef boost::mpl::vector<double> type;
};
/// @brief Helper type that makes getting the constructor argument slightly
/// easier.
template <typename Vector,
std::size_t Index>
struct get_constructor_arg
: boost::mpl::at<Vector, boost::mpl::int_<Index> >
{};
/// @brief runner type is used to provide a static run function that
/// will delegate the construction and running of type T based
/// on T's constructor_traits.
template <typename T,
typename Args = typename constructor_traits<T>::type,
std::size_t = boost::mpl::size<Args>::value>
struct runner
{
static void run()
{
T().Run();
}
};
/// Specialization for runner for types with have a single argument
/// constructor.
template <typename T,
typename Args>
struct runner<T, Args, 1>
{
static void run(typename get_constructor_arg<Args, 0>::type a0)
{
T(a0).Run();
}
};
BOOST_PYTHON_MODULE(example)
{
boost::python::def("f1", &runner<C1>::run);
boost::python::def("f2", &runner<C2>::run);
}
并测试输出:
>>> import example
>>> example.f1()
run c1
>>> example.f2(3.14)
run c2: 3.14