考虑以下情况,其中基类规定了可以在派生类中重新实现的函数:
template<typename Impl>
class Base {
public:
void f(double& value, double param)
{ value = 0.0; }
};
class Implementation : public Base<Implementation> {
public:
void f(double& value, double param)
{ value = param; }
};
int main()
{
Implementation imp;
double value = 0.0;
imp.f(value, 1.0);
}
假设我想将基类中函数f
的签名更改为double f(double param)
,并将其用于Base
和派生类。我想在不立即破坏所有实现类的情况下执行此更改。相反,实现类的提供者应该获得弃用警告。我想出了以下内容:
template<typename Impl>
class Base {
public:
double f(double param) __attribute__ ((deprecated))
{
double v = 0.0;
static_cast<Impl*>(this)->f(v, param);
return v;
}
void f(double& value, double param)
{ value = 0.0; }
};
// class Implementation as before
int main()
{
Implementation imp;
double value = imp.f(1.0);
}
因此,只有在Base
未使用新签名重新实现f
时才会调用f
Implementation
版{@ 1}},从而产生所需的弃用警告
显然,这不会编译,因为f
中Implementation
的定义会影响f
中的Base
:
error: no matching function for call to ‘Implementation::f(double)’
double value = imp.f(1.0);
一种解决方案是添加using
:
class Implementation : public Base<Implementation> {
public:
using Base<Implementation>::f;
void f(double& value, double param)
{ value = param; }
};
这将迫使Implementation
的提供者立即更改他的代码,这正是首先应该避免的代码。另一种可能性是在新签名中更改名称f
。我也想避免这种情况,因为f
在我的真实用例中有一个非常好的名字。
所以我的问题是:我可以执行签名更改
Implementation
的提供商会收到弃用警告,但其代码不会立即中断,即不更改Implementation
和f
?答案 0 :(得分:0)
我可以构建一个适合我的解决方案,从jrok's answer到问题Check if a class has a member function of a given signature:
#include <iostream>
#include <type_traits>
template<typename, typename T>
struct hasFunctionF {};
template<typename C, typename Ret, typename... Args>
struct hasFunctionF<C, Ret(Args...)> {
private:
template<typename T>
static constexpr auto check(T*)
-> typename
std::is_same<
decltype( std::declval<T>().f( std::declval<Args>()... ) ),
Ret // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>::type; // attempt to call it and see if the return type is correct
template<typename>
static constexpr std::false_type check(...);
typedef decltype(check<C>(0)) type;
public:
static constexpr bool value = type::value;
};
template<typename Impl>
class Base {
public:
template<class T = Impl>
__attribute__ ((deprecated))
const typename std::enable_if_t<hasFunctionF<T, void(double&, double)>::value, double> g(double param)
{
double v = 0.0;
static_cast<Impl*>(this)->f(v, param);
return v;
}
template<class T = Impl>
const typename std::enable_if_t<hasFunctionF<T, double(double)>::value, double> g(double param)
{
return static_cast<Impl*>(this)->f(param);
}
};
class OldImplementation : public Base<OldImplementation> {
public:
void f(double& value, double param)
{ value = param; }
};
class NewImplementation : public Base<NewImplementation> {
public:
double f(double param)
{ return param; }
};
int main()
{
OldImplementation oldImp;
double value = oldImp.g(1.0);
std::cout << "old: " << value << std::endl;
NewImplementation newImp;
std::cout << "new: " << newImp.g(1.0) << std::endl;
}
我仍然需要在基类中引入一个新的函数名g
并使用该名称进行调用。但我不认为如果不改变用户代码就可以避免这种情况。用户代码为OldImplementation
,不得立即更改。使用旧接口编译用户代码仅提供弃用警告。这正是我想要实现的目标。