2

我试图让程序员(使用我的库)创建可命名的类型实例,X这些实例存储在类的实例中C(或者至少是该实例独有的)。这是我设法想出的仅有的两个(丑陋的)解决方案(不用说,我只是在学习 C++)

1)

class C
{
public:
    class XofC
    {
    public:
        XofC() = delete;
        XofC(C& mom)
        {
            mom.Xlist.emplace_front();
            ref = Xlist.front();
        }
        X& access()
        {
            return ref;
        }
    private:
        X& ref;
    };
    //etc
private:
    std::forward_list<X> Xlist;
    friend class XofC;
    //etc
}

问题:
必须到处传递 XofC 实例。

2)

class C
{
public:
    void newX(std::string);
    X& getX(std::string);
    //etc.
private:
    /*possible run-time mapping implementation
    std::vector<X> Xvec;
    std::unordered_map<std::string, decltype(Xvec.size())> NameMap;
    */
    //etc
}

问题:
这可以完成这项工作,但是由于 X ( std::string) 的所有名称在编译时都是已知的,因此使用运行时的开销std::unordered_map<std::string, decltype(Xvec.size())>让我对这种简单的事情感到厌烦。

可能的()解决方案:编译时替换为std::string自动索引(int)。然后我可以使用:

class C
{
public:
    void newX(int); //int: unique index calculated at compile time from std::string
    X& getX(int); //int: unique index calculated at compile time from std::string
//etc.
private:
    std::vector<X> Xvec;
}

问题:

  1. 3)吗?
  2. 2)的编译时解决方案是否可行?
  3. 这是现实生活中的情况:我开始了我的第一个 C++“项目”,我认为我可以使用一个很棒的用户友好、简单和快速的参数管理库中的实践和实用程序。我计划制作一个可以根据一些指定的开关ArgMan解析的类。argV开关将由程序员描述性地命名并指定触发字符串(例如,名为递归的开关可以具有"-r""-recursive"作为触发器)。必要时,您应该能够轻松获得开关的设置。实施细节:ArgMan会有一个std::unordered_map<std::string/*a trigger*/, ??/*something linking to the switch to set on*/>. 这确保了argV相对于的几乎线性解析argC。我应该如何处理这个?
4

2 回答 2

1

您可以“滥用”非类型模板参数来获取编译时命名实例:

假设我们有一个数据类X

#include <string> 

struct X
{
    int         has_some_properties;
    std::string data;
};

现在,对于我们的命名实例,我们定义了一些名称常量。诀窍是,为它们提供外部链接,因此我们可以将地址用作非类型模板参数。

// define some character arrays **with external linkage**
namespace Names
{
    extern const char Vanilla[] = "Vanilla";
    extern const char Banana [] = "Banana";
    extern const char Coconut[] = "Coconut";
    extern const char Shoarma[] = "Shoarma";
}

现在,我们制作一个接受非类型模板参数的NamedX包装器。const char*包装器包含()的静态实例。X

// now we can "adorn" a `namedX` with the name constants (above)
template <const char* Name>
   struct NamedX
{
    static X value;
};

template <const char* Name> X NamedX<Name>::value;

现在你可以像这样使用它:

int main()
{
    X& vanilla = NamedX<Names::Vanilla>::value;

    vanilla = { 42, "Woot!" };

    return vanilla.has_some_properties;
}

请注意,由于模板参数是地址,因此没有进行实际的字符串比较。你不能,例如使用

    X& vanilla = NamedX<"Vanilla">::value;

因为"Vanilla"是没有外部链接的prvalue。所以,事实上你可以不用一些复杂性而使用标签结构来代替:Live on Coliru

于 2013-09-20T15:30:36.100 回答
0

虽然 Neil 的解决方案符合我的要求,但在我的库中使用它太花哨了。此外,sehe 的技巧肯定很有用,但是,如果我理解正确,但似乎与我的问题无关。我决定使用方法 1) 来模拟所需的行为,这是一个不太成功的尝试:

class C
{
private:
    class X
    {
        //std::string member;
        //etc
    };
public:
    class XofC
    {
    public:
        XofC(C & _mom) : mom(_mom)
        {
            mom.Xlist.emplace_front();
            tehX = &(Xlist.front());
        }
        X & get(maybe)
        {
            if (&maybe != &mom) throw std::/*etc*/;
            return &tehX;
        }
    private:
        X * tehX;
        C & mom;
    };
private:
    //etc
    std::forward_list<X> Xlist;
    friend class XofC;
    //etc
};

用法:

C foo;
bar = C::XofC(foo); //acts like an instance of X, but stored in C, but you have to use:
bar.get(foo)/*reference to the actual X*/.member = "_1_";

当然,缺点是您必须确保在需要的任何地方都通过 bar,但工作得体。这就是它在我的小参数管理器库中的样子: https ://raw.github.com/vuplea/arg_manager.h/master/arg_manager.h

于 2013-09-29T13:20:08.433 回答