C++ 我可以有模板实例化的副作用吗?

C++ 我可以有模板实例化的副作用吗?,c++,templates,instantiation,C++,Templates,Instantiation,所以,让我们从我想要的代码开始,但是我想教它一个新的技巧(如果可能的话) 我想要的是,在第一次调用printMe函数之前,只有那些用来“注册”自己的类。因此,输出将如下所示: Registering class B Registering class C Printing class B Printing class B Printing class C 表面上看,这似乎是可能的。编译器“知道”用于实例化的类型。如果我能够将这些信息存储在一些全局或静态的东西中,那么我只需要在mai

所以,让我们从我想要的代码开始,但是我想教它一个新的技巧(如果可能的话)

我想要的是,在第一次调用
printMe
函数之前,只有那些用来“注册”自己的类。因此,输出将如下所示:

Registering class B
Registering class C
Printing class B   
Printing class B   
Printing class C
表面上看,这似乎是可能的。编译器“知道”用于实例化的类型。如果我能够将这些信息存储在一些全局或静态的东西中,那么我只需要在
main()
的开头处理它

但是到目前为止,我所有颠覆编译器的尝试都失败了。这让我怀疑这是故意的。那么“我可以有模板实例化的副作用吗?”他问道,期待着回答‘不’


编辑:对不起,我真的没能用那句话清楚地表达自己。在上面的例子中,我不需要注册
A、B、C和D
——我只需要注册
B和C
,我想让编译器自己解决这个问题。

你可以通过创建一个助手类来实现,这个助手类的构造函数负责注册这些类。然后在外部作用域中创建helper类的实例

#include <string>
#include <iostream>

class A {}; class B {}; class C {}; class D {};

template<typename T> struct traits { };
template<> struct traits<A> { static std::string code() {return "class A";}};
template<> struct traits<B> { static std::string code() {return "class B";}};
template<> struct traits<C> { static std::string code() {return "class C";}};
template<> struct traits<D> { static std::string code() {return "class D";}};

template<typename T> 
void registerClass(){
    std::cout << "Registering " << traits<T>::code() << '\n';
}

template<typename T> void printMe() {
    std::cout << "Printing " << traits<T>::code() << '\n';
}

struct Init
{
   Init();
};

Init::Init()
{
   registerClass<A>();
   registerClass<B>();
   registerClass<C>();
   registerClass<D>();
}

static Init init;

int main(void)
{
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 
    return 0;
}
#包括
#包括
A类{};B类{};C类{};D类{};
模板结构特征{};
模板结构特征{static std::string code(){return“class A”;};
模板结构特征{static std::string code(){return“class B”;};
模板结构特征{static std::string code(){return“class C”;};
模板结构特征{static std::string code(){return“class D”;};
模板
无效注册表类(){

std::cout添加一个静态成员变量并实例化它

namespace {
    template<typename T>
    class registrar {
    public:
        registrar() {
            std::string t = RegisterClass(traits<T>::code());
        }
    };
}
template <class type> struct traits {
    virtual std::string code() = 0; // override this
private:
    static registrar m_registrar;
};
registrar traits<A>::m_registrar;
名称空间{
模板
班级登记员{
公众:
注册主任(){
std::string t=RegisterClass(traits::code());
}
};
}
模板结构特征{
虚拟std::字符串代码()=0;//重写此
私人:
静态注册器m_注册器;
};
注册者特征:m_注册者;

通过这种方式,对于
traits
而言,任何给定的
T
都将因此产生一个副作用,即在运行时启动时调用
register::register()

是,使用在命名空间范围中实例化的静态成员变量。例如:

template<typename T>
struct RegisterClass
{
    static RegisterClass instance;
    RegisterClass()
    {
        std::cout << "Registering " << traits<T>::code() << '\n';
    }
    static RegisterClass& doRegister() { return instance; }
};
template<typename T>
RegisterClass<T> RegisterClass<T>::instance;

template<typename T> void printMe() {
    RegisterClass<T>::doRegister();
    std::cout << "Printing " << traits<T>::code() << '\n';
}
#include <string>
#include <iostream>
#include <typeinfo>

// Forward declaration for "traits" wrapper class.  If you wanted to, you 
// could to change the self_registering template to not use a wrapper class, 
// default a wrapper but allow other specializations, etc.
template<typename T> struct traits;

template<typename wrappedClass>
struct self_registering {
    self_registering() {};
    virtual ~self_registering() {};
    static bool isRegistered() { return registered; }

private:
    typedef traits<wrappedClass> WrappedClass_;
    static bool doRegistration() {
        std::cout << "Default-registering " << WrappedClass_().code() << '\n';
        return true;
    };
    static const bool registered;
};
template<typename T>
const bool self_registering<T>::registered 
         = self_registering<T>::WrappedClass_().doRegistration();

// our traits wrapper class...
template<typename T> 
struct traits : self_registering<T>   {
    static std::string code() { return typeid(T).name(); };
};

// Well, that's pretty much it. Time to use it:

// a few classes we're going to (maybe) want self-registered...
class A {}; class B {}; class C {}; class D {};
class E {}; class F {}; class G {}; class H {};

// provide custom registration for class H:
template<> struct traits<H> : self_registering<H> { 
    static std::string code() { return "class H"; }

    bool doRegistration() {
        std::cout << "Private-registering " << code() << "\n";
        // do something here like H::register()...
        return true;
    };
};

template<typename T>
void printMe() {
    static bool isRegistered = traits<T>::isRegistered();
    (void)isRegistered; // shut gcc up about unused variable
    std::cout << "Printing " << traits<T>::code() << '\n';
}

int main(void)
{
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 

    printMe<H>(); 

    return 0;
}
模板
结构寄存器类
{
静态注册类实例;
寄存器类()
{

std::cout这是可行的,但这是一个糟糕的答案,因为我不知道为什么静态变量如此奇怪

#include <iostream>
using namespace std;

template <typename T>
class Registrar {
public:
    Registrar();
};

template <typename D>
struct SelfRegistrar {
   static Registrar<D> d_;
    SelfRegistrar(){
        cout << &d_ << endl;
    }
};



//for some reason I don't understand need some reference to d, opening another question...
class A : SelfRegistrar<A> { static void* foo() {return &d_;} };
class B : SelfRegistrar<B> { static void* foo() {return &d_;}};
class C : SelfRegistrar<C> { static void* foo() {return &d_;}};
class D : SelfRegistrar<D> { static void* foo() {return &d_;}};

template<typename T> struct traits { };
template<> struct traits<A> { static std::string code() {return "class A";}};
template<> struct traits<B> { static std::string code() {return "class B";}};
template<> struct traits<C> { static std::string code() {return "class C";}};
template<> struct traits<D> { static std::string code() {return "class D";}};

template <typename D>
Registrar<D> SelfRegistrar<D>::d_{};

std::string RegisterClass(const std::string & c){
    std::cout << "Registering " << c << '\n';
    return c;
}

template<typename T> void printMe() {
    std::cout << "Printing " << traits<T>::code() << '\n';
}

template <typename D>
Registrar<D>::Registrar()
{
   std::string c = traits<D>::code();
   std::cout << "Registering " << c << '\n';
}

int main() {
    // your code goes here
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 
    return 0;
}
#包括
使用名称空间std;
模板
班级登记员{
公众:
注册主任();
};
模板
结构自注册器{
静态注册器d_;
自我登记(){

cout有时,从基类派生比使用嵌入式或附加的帮助器类更方便。根据您的需要,这种方法有时会使事情变得更简单/更干净,更易于重用

例如:

template<typename T>
struct RegisterClass
{
    static RegisterClass instance;
    RegisterClass()
    {
        std::cout << "Registering " << traits<T>::code() << '\n';
    }
    static RegisterClass& doRegister() { return instance; }
};
template<typename T>
RegisterClass<T> RegisterClass<T>::instance;

template<typename T> void printMe() {
    RegisterClass<T>::doRegister();
    std::cout << "Printing " << traits<T>::code() << '\n';
}
#include <string>
#include <iostream>
#include <typeinfo>

// Forward declaration for "traits" wrapper class.  If you wanted to, you 
// could to change the self_registering template to not use a wrapper class, 
// default a wrapper but allow other specializations, etc.
template<typename T> struct traits;

template<typename wrappedClass>
struct self_registering {
    self_registering() {};
    virtual ~self_registering() {};
    static bool isRegistered() { return registered; }

private:
    typedef traits<wrappedClass> WrappedClass_;
    static bool doRegistration() {
        std::cout << "Default-registering " << WrappedClass_().code() << '\n';
        return true;
    };
    static const bool registered;
};
template<typename T>
const bool self_registering<T>::registered 
         = self_registering<T>::WrappedClass_().doRegistration();

// our traits wrapper class...
template<typename T> 
struct traits : self_registering<T>   {
    static std::string code() { return typeid(T).name(); };
};

// Well, that's pretty much it. Time to use it:

// a few classes we're going to (maybe) want self-registered...
class A {}; class B {}; class C {}; class D {};
class E {}; class F {}; class G {}; class H {};

// provide custom registration for class H:
template<> struct traits<H> : self_registering<H> { 
    static std::string code() { return "class H"; }

    bool doRegistration() {
        std::cout << "Private-registering " << code() << "\n";
        // do something here like H::register()...
        return true;
    };
};

template<typename T>
void printMe() {
    static bool isRegistered = traits<T>::isRegistered();
    (void)isRegistered; // shut gcc up about unused variable
    std::cout << "Printing " << traits<T>::code() << '\n';
}

int main(void)
{
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 

    printMe<H>(); 

    return 0;
}
和gcc 4.7.2,如下所示:

$ gcc -std=c++11 -lstdc++ -Wall -pedantic so-misc.cpp
$ ./a.out | c++filt -t
Default-registering B
Default-registering C
Private-registering class H
Printing B
Printing B
Printing C
Printing class H

显然,您还可以做其他事情(进一步模板化
自注册
,使包装类比
traits
更通用,进行一些清理等)“但是,这可能对你来说是一个开始。”/P>你能负担得起使用宏/预处理器吗?@ OGLU,很显然,我喜欢纯的。但是如果你有工业上的固体,我会考虑时间的考验。工业固体测试时间解决方案是使用一个寄存器宏来做登记,比如MO。大多数测试框架都有。但是,有一个家伙有一个解决方案(尽管是在未定义的行为方面),它可以按照您的意愿工作(检查最后的答案)。这解决了问题,但没有回答问题(可能只是学术性的,但在我看来很有趣)实际问题。与我的答案类似。但是这需要将每种类型的
T
添加到
Init
中。从好的方面来说,这意味着他可以有选择性地产生副作用,实例化每种
T
,而不是自动产生副作用。@RSahu我真的很抱歉,我把你弄糊涂了——看到我的答案了吗编辑。(不过,您可能会注意到代码的输出与所需的输出不一致。)如果有人在构建
实例之前访问它怎么办?通常,静态局部变量的作用是防止初始化错误的顺序。@MarkB
实例
可以是私有的。@MarkB在静态对象中仍然完全可能有初始化错误的顺序,事实上许多人在thr中错误地滥用静态对象eaded CODE静态变量有什么奇怪的?我认为基本变量不需要是virtual@B正如目前所写的,可能不是,但因为它是从中派生出来的,我只是理所当然地使用了一个虚拟析构函数。
$ gcc -std=c++11 -lstdc++ -Wall -pedantic so-misc.cpp
$ ./a.out | c++filt -t
Default-registering B
Default-registering C
Private-registering class H
Printing B
Printing B
Printing C
Printing class H