SFINAE 使用模板、专业化和实现类型擦除
SFINAE using templates, specialization, and achieving type erasure
我最近遇到了一个有趣的 SFINAE 习语实现,但我在使用专业化 classes 时遇到了麻烦。让我用一个简化的例子来解释。我在下面的主要函数中使用了 4 个 classes-- Base
、Variant_
、English
、Japanese
.
#include <iostream>
#include <memory>
int main() {
Variant_<English> MrRobert;
Variant_<Japanese> MrRoboto;
std::cout << "Mr. Robert: ";
MrRobert.dispatch_say_hi();
std::cout << "Mr. Roboto: ";
MrRoboto.dispatch_say_hi();
return 0;
}
输出:
Mr. Robert: Hello World!
Mr. Roboto: Konnichiwa Sekkai!
Variant_
是一个模板 class,派生自 Base
,后者是一个抽象 class。 English
和 Japanese
是专业化 classes 并且不派生自任何东西。
基地:
class Base
{
public:
Base(){}
virtual ~Base(){}
protected:
virtual void dispatch_say_hi()=0;
};
变体_:
template<class T>
struct has_f
{
typedef char yes;
typedef char (&no)[2];
template<class U>
static yes test_say_hi(__typeof__(&U::say_hi));
template<class U>
static no test_say_hi(...);
static const int say_hi = sizeof(test_say_hi<T>(0)) == sizeof(yes);
};
template<class Impl>
class Variant_: private Base
{
std::unique_ptr<Impl> impl_;
public:
template<int I> struct int_{};
typedef int_<0> not_implemented;
typedef int_<1> implemented;
void say_hi(not_implemented){
std::cout << "..." << std::endl;
}
void say_hi(implemented){
impl_->say_hi();
}
void dispatch_say_hi(){
say_hi(int_<has_f<Impl>::say_hi>());
}
};
好吧,我撒谎了。还有第五个classhas_f
。它是一个帮助程序 class 来识别一个函数是否已经在专业化中定义。那不是我遇到问题的地方,但我已经提供了它,以便可以编译此示例并 运行.
英文:
class English
{
public:
void say_hi(){
std::cout<<"Hello World!"<< std::endl;
}
};
日语:
class Japanese
{
public:
void say_hi(){
std::cout<<"Konnichiwa Sekkai!"<<std::endl;
}
};
这一切编译和运行s.
这是我的问题:
假设我修改了 Japanese
.
class Japanese
{
private:
std::string something_else;
public:
void say_hi(){
std::cout<<"Konnichiwa Sekkai!";
something_else = " bleep bloop"; //segfault!
std::cout<<something_else<<std::endl;
}
};
现在,当我尝试 运行 main()
时,我在 MrRoboto.dispatch_say_hi()
处遇到了分段错误。成员变量是private还是public,是int还是string都没有关系。每当我尝试访问任何成员变量时,我都会遇到分段错误。为什么会这样?
有趣的是,如果我定义另一个函数,我可以这样调用它:
class Japanese
{
public:
void say_hi(){
std::cout<<"Konnichiwa Sekkai!";
say_something_else();
std::cout<<std::endl;
}
void say_something_else()
{
std::cout<<" bleep bloop";
}
};
这个输出是:
Mr. Robert: Hello World!
Mr. Roboto: Konnichiwa Sekkai! bleep bloop
有什么想法吗?
您有一个从未设置的 unique_ptr<impl>
,因此它保持初始默认值,即 nullptr
。它之前有效,因为你不是 Japanese
除了函数之外的任何东西(注意:你有取消引用空指针的未定义行为,即使在你的情况下大多数实现都可以正常工作)。
请注意,您所做的称为 "Policy pattern"。它有时非常有用,而且可能很有趣,但是请注意,这种模式的一大缺点是缺少界面——没有任何东西可以告诉用户要实现什么功能,因此他们必须查看代码。
我最近遇到了一个有趣的 SFINAE 习语实现,但我在使用专业化 classes 时遇到了麻烦。让我用一个简化的例子来解释。我在下面的主要函数中使用了 4 个 classes-- Base
、Variant_
、English
、Japanese
.
#include <iostream>
#include <memory>
int main() {
Variant_<English> MrRobert;
Variant_<Japanese> MrRoboto;
std::cout << "Mr. Robert: ";
MrRobert.dispatch_say_hi();
std::cout << "Mr. Roboto: ";
MrRoboto.dispatch_say_hi();
return 0;
}
输出:
Mr. Robert: Hello World!
Mr. Roboto: Konnichiwa Sekkai!
Variant_
是一个模板 class,派生自 Base
,后者是一个抽象 class。 English
和 Japanese
是专业化 classes 并且不派生自任何东西。
基地:
class Base
{
public:
Base(){}
virtual ~Base(){}
protected:
virtual void dispatch_say_hi()=0;
};
变体_:
template<class T>
struct has_f
{
typedef char yes;
typedef char (&no)[2];
template<class U>
static yes test_say_hi(__typeof__(&U::say_hi));
template<class U>
static no test_say_hi(...);
static const int say_hi = sizeof(test_say_hi<T>(0)) == sizeof(yes);
};
template<class Impl>
class Variant_: private Base
{
std::unique_ptr<Impl> impl_;
public:
template<int I> struct int_{};
typedef int_<0> not_implemented;
typedef int_<1> implemented;
void say_hi(not_implemented){
std::cout << "..." << std::endl;
}
void say_hi(implemented){
impl_->say_hi();
}
void dispatch_say_hi(){
say_hi(int_<has_f<Impl>::say_hi>());
}
};
好吧,我撒谎了。还有第五个classhas_f
。它是一个帮助程序 class 来识别一个函数是否已经在专业化中定义。那不是我遇到问题的地方,但我已经提供了它,以便可以编译此示例并 运行.
英文:
class English
{
public:
void say_hi(){
std::cout<<"Hello World!"<< std::endl;
}
};
日语:
class Japanese
{
public:
void say_hi(){
std::cout<<"Konnichiwa Sekkai!"<<std::endl;
}
};
这一切编译和运行s.
这是我的问题:
假设我修改了 Japanese
.
class Japanese
{
private:
std::string something_else;
public:
void say_hi(){
std::cout<<"Konnichiwa Sekkai!";
something_else = " bleep bloop"; //segfault!
std::cout<<something_else<<std::endl;
}
};
现在,当我尝试 运行 main()
时,我在 MrRoboto.dispatch_say_hi()
处遇到了分段错误。成员变量是private还是public,是int还是string都没有关系。每当我尝试访问任何成员变量时,我都会遇到分段错误。为什么会这样?
有趣的是,如果我定义另一个函数,我可以这样调用它:
class Japanese
{
public:
void say_hi(){
std::cout<<"Konnichiwa Sekkai!";
say_something_else();
std::cout<<std::endl;
}
void say_something_else()
{
std::cout<<" bleep bloop";
}
};
这个输出是:
Mr. Robert: Hello World!
Mr. Roboto: Konnichiwa Sekkai! bleep bloop
有什么想法吗?
您有一个从未设置的 unique_ptr<impl>
,因此它保持初始默认值,即 nullptr
。它之前有效,因为你不是 Japanese
除了函数之外的任何东西(注意:你有取消引用空指针的未定义行为,即使在你的情况下大多数实现都可以正常工作)。
请注意,您所做的称为 "Policy pattern"。它有时非常有用,而且可能很有趣,但是请注意,这种模式的一大缺点是缺少界面——没有任何东西可以告诉用户要实现什么功能,因此他们必须查看代码。