【发布时间】:2017-12-11 19:29:43
【问题描述】:
我正在使用启用了 C++17 支持的 VS2017。
我想尝试创建一个“Transformer”类,当提供某种受支持的类型时,它将转换类型,否则它将按原样返回变量。目标是将所有变量类型传递给转换器,并“隐藏”它的转换类型。这样调用者可以尝试转换所有内容,而不必担心是否需要转换,转换器会知道。
一个更完整的例子(根据原文编辑):
class MyPoint
{
public:
int x = 0;
};
class NotMyPoint
{
public:
int x = 50;
};
template <typename T>
class ITransform
{
public:
virtual ~ITransform() {};
virtual T InTransform(const T &in) const = 0;
virtual T OutTransform(const T &out) const = 0;
//Check if the argument type is the same as this class type
template <typename X>
constexpr bool CanTransform() const
{
return std::is_same<X, T>::value;
}
};
class MyTransformer :
public ITransform<MyPoint>
{
public:
MyTransformer() = default;
virtual MyPoint InTransform(const MyPoint &in) const override
{
auto newPt = in;
newPt.x += 100;
return newPt;
}
virtual MyPoint OutTransform(const MyPoint &in) const override
{
auto newPt = in;
newPt.x -= 100;
return newPt;
}
};
template <class... TRANSFORMERS>
struct VariadicTransformer
{
constexpr VariadicTransformer() = default;
/** \brief parse using validateParse but catch throw */
template <typename T>
inline T Transform(const T& in)
{
return TransformImpl<sizeof...(TRANSFORMERS)-1, T>(in);
}
private:
/// last attempt to find matching transformer at I==0, if it fails return the original value
template<std::size_t I = 0, typename T>
inline typename std::enable_if<I == 0, T>::type TransformImpl(const T &in) const
{
if (std::get<I>(transformers).CanTransform<T>())
return std::get<I>(transformers).InTransform(in);
else
return in;
}
/// attempt to find transformer for this type
template<std::size_t I = 0, typename T>
inline typename std::enable_if < I != 0, T>::type TransformImpl(const T &in) const
{
if (std::get<I>(transformers).CanTransform<T>())
return std::get<I>(transformers).InTransform(in);
else
return TransformImpl<I - 1, T>(in);
}
std::tuple<const TRANSFORMERS...> transformers;
};
//Example usage
VariadicTransformer<MyTransformer, MyTransformer> varTrans;
MyPoint myPoint;
NotMyPoint notMyPoint;
std::cout << myPoint.x << std::endl;
myPoint = varTrans.Transform(myPoint);
std::cout << myPoint.x << std::endl;
std::cout << notMyPoint.x << std::endl;
notMyPoint = varTrans.Transform<NotMyPoint>(notMyPoint);
std::cout << notMyPoint.x << std::endl;
return 0;
我的问题在于这一行:
if constexpr(std::get<I>(transformers).CanTransform<T>())
这将无法编译并提供以下错误:
错误 C2131:表达式未计算为常量
注意:失败是由在其生命周期之外读取变量引起的
注意:查看“this”的用法
CanTransform 函数应该是 constexpr,std::get(std::tuple) 应该是 constexpr,所以我不确定它对这一行的抱怨是什么。
还需要 if constexpr 以避免尝试调用任何不适合转换当前类型的转换器,我希望这种情况能够通过并返回原始类型。
关于导致此错误的原因或我可以尝试其他设计的任何建议?
【问题讨论】:
-
std::get<I>(this->transformers).CanTransform<T>()→std::get<I>(this->transformers).template CanTransform<T>() -
return constexpr(...);不是有效的 C++。 -
@chris 是 Visual C++ 扩展还是错误?我无法快速找到有关此类构造的任何信息。
-
@Constructor,除了提案中的
constexpr运算符外,我没有遇到过任何类似的情况。 -
只需将
CanTransform设为静态即可。它不访问任何成员,因此没有理由需要是非静态的。如果它是静态的,则不再需要*this对象constexpr。
标签: c++ templates c++14 variadic-templates c++17