3

Given the following template:

template <typename T>
class wrapper : public T {};

What visible differences in interface or behaviour are there between an object of type Foo and an object of type wrapper<Foo>?

I'm already aware of one:

  • wrapper<Foo> only has a nullary constructor, copy constructor and assignment operator (and it only has those if those operations are valid on Foo). This difference may be mitigated by having a set of templated constructors in wrapper<T> that pass values through to the T constructor.

But I'm not sure what other detectable differences there might be, or if there are ways of hiding them.


(Edit) Concrete Example

Some people seem to be asking for some context for this question, so here's a (somewhat simplified) explanation of my situation.

I frequently write code which has values which can be tuned to adjust the precise performance and operation of the system. I would like to have an easy (low code overhead) way of exposing such values through a config file or the user interface. I am currently writing a library to allow me to do this. The intended design allows usage something like this:

class ComplexDataProcessor {
    hotvar<int> epochs;
    hotvar<double> learning_rate;
public:
    ComplexDataProcessor():
        epochs("Epochs", 50),
        learning_rate("LearningRate", 0.01)
        {}

    void process_some_data(const Data& data) {
        int n = *epochs;
        double alpha = *learning_rate;
        for (int i = 0; i < n; ++i) {
            // learn some things from the data, with learning rate alpha
        }
    }
};

void two_learners(const DataSource& source) {
    hotobject<ComplexDataProcessor> a("FastLearner");
    hotobject<ComplexDataProcessor> b("SlowLearner");
    while (source.has_data()) {
        a.process_some_data(source.row());
        b.process_some_data(source.row());
        source.next_row();
    }
}

When run, this would set up or read the following configuration values:

FastLearner.Epochs
FastLearner.LearningRate
SlowLearner.Epochs
SlowLearner.LearningRate

This is made up code (as it happens my use case isn't even machine learning), but it shows a couple of important aspects of the design. Tweakable values are all named, and may be organised into a hierarchy. Values may be grouped by a couple of methods, but in the above example I just show one method: Wrapping an object in a hotobject<T> class. In practice, the hotobject<T> wrapper has a fairly simple job -- it has to push the object/group name onto a thread-local context stack, then allow the T object to be constructed (at which point the hotvar<T> values are constructed and check the context stack to see what group they should be in), then pop the context stack.

This is done as follows:

struct hotobject_stack_helper {
    hotobject_stack_helper(const char* name) {
        // push onto the thread-local context stack
    }
};

template <typename T>
struct hotobject : private hotobject_stack_helper, public T {
    hotobject(const char* name):
        hotobject_stack_helper(name) {
        // pop from the context stack
    }
};

As far as I can tell, construction order in this scenario is quite well-defined:

  1. hotobject_stack_helper is constructed (pushing the name onto the context stack)
  2. T is constructed -- including constructing each of T's members (the hotvars)
  3. The body of the hotobject<T> constructor is run, which pops the context stack.

So, I have working code to do this. There is however a question remaining, which is: What problems might I cause for myself further down the line by using this structure. That question largely reduces to the question that I'm actually asking: How will hotobject behave differently from T itself?

4

4 に答える 4

3

特定の使用法について質問する必要があるため、奇妙な質問です(「私は何をしたいのか、これは私をどのように助けたり傷つけたりしますか」)が、一般的には次のようになります。

wrapper<T>ではないTので、次のようになります。

  • のように構築することはできませんT。(ご指摘の通りです。)
  • のように変換することはできませんT
  • アクセスできるプライベートへのアクセスを失いますT

他にもあると思いますが、最初の 2 つでかなりカバーできます。

于 2010-08-25T04:51:42.000 に答える
2

あなたが持っていると仮定します:

class Base {};
class Derived : Base {};

今、あなたは言うことができます:

Base *basePtr = new Derived;

ただし、次のように言うことはできません。

wrapper<Base> *basePtr = new wrapper<Derived>();

つまり、型パラメーターに継承関係がある場合でも、テンプレートを特殊化して生成された 2 つの型には継承関係がありません。

于 2010-08-25T05:39:05.700 に答える
0

オブジェクトへの参照は、基本クラスのサブオブジェクトへの参照に変換できます (アクセス権が与えられます)。オブジェクトをベースのインスタンスとして扱うことを可能にする暗黙的な変換を呼び出すための構文糖衣がありますが、それが実際に起こっていることです。それ以上でもそれ以下でもありません。

したがって、違いを検出するのはまったく難しくありません。それらは(ほぼ)完全に異なるものです。「is-a」関係と「has-a」関係の違いは、メンバー名を指定することです。

基本クラスを非表示にすることについては、うっかり自分の質問に答えたと思います。privateを指定して(または a を省略publicして)プライベート継承を使用するclassと、これらの変換はクラス自体の外部では発生せず、他のクラスはベースが存在することさえ認識できなくなります。

于 2010-08-25T04:51:46.147 に答える
0

継承されたクラスに独自のメンバー変数 (または少なくとも 1 つ) がある場合、

sizeof(InheritedClass) > sizeof(BaseClass)
于 2010-08-25T04:53:34.500 に答える