【问题标题】:Type injection in structs结构中的类型注入
【发布时间】:2017-03-17 08:48:17
【问题描述】:

我认为这不可能,但我想完全确定,所以我还是问了..

我想从一个没有在模板中传递但以其他方式注入的模板化结构(可用于 constexpr 函数)获取编译时间值。

很难解释,我用一些代码试试:

template<int A>
struct MagicStruct
{
enum { current = A, injected = /* magic */}
};

template<int A, int B>
struct InjectionStruct
{
enum { first=A, second=B}
/*... injection of B in MagicStruct<A> ... */
};

static const int AVALUE = 1;
static const int BVALUE = 2;

static const int CVALUE = InjectionStruct<AVALUE, BVALUE>::first; //== 1
static const int CVALUE = InjectionStruct<AVALUE, BVALUE>::second; //== 2
static const int DVALUE = MagicStruct<AVALUE>::injection; //== 2

是否有一些我不知道的技巧会允许这样做?

[edit] 我想只在输入中使用 AVALUE 作为模板参数来获取 DVALUE

【问题讨论】:

  • 关于你的edit:如果你有20个InjectionStruct,哪个定义了你的MagicStructinjected值?正如我的回答中所解释的,您需要在MagicStruct 和提供数据的InjectionStruct 之间建立关系。
  • 好点 e 复杂的问题 :) 我知道这没有多大意义.. 但我认为它应该把评估/声明/定义/无论如何作为最后一个..

标签: c++ templates traits template-meta-programming constexpr


【解决方案1】:

您可以将 injected 改为 static int

template<int A>
struct MagicStruct
{
    enum { current = A };
    static int injected;
};

template<int A>
int MagicStruct<A>::injected;

然后给InjectionStruct一个静态成员,其实例化将填写MagicStruct&lt;A&gt;::injected

template<int A, int B>
struct InjectionStruct : MagicStruct<A>
{
    enum { first=A, second=B};

    struct filler {
        filler() { MagicStruct<A>::injected = B; }
    };

    static filler inject;
};

template <int A, int B>
typename InjectionStruct<A,B>::filler InjectionStruct<A,B>::inject;

然后进行注入,你只需要在某处使用inject,或者显式实例化它:

static const int AVALUE = 1;
static const int BVALUE = 2;

static const int CVALUE = InjectionStruct<AVALUE, BVALUE>::first; //== 1
static const int DVALUE = InjectionStruct<AVALUE, BVALUE>::second; //== 2
//explicit instantiation
template InjectionStruct<AVALUE,BVALUE>::filler
         InjectionStruct<AVALUE,BVALUE>::inject;
static const int EVALUE = MagicStruct<AVALUE>::injected; //== 2

或者,您可以在某些用于检索firstsecond 的函数中隐藏injected 的实例化。


这是一个疯狂的实现,它依赖于通过模板实例化注入函数的实现。如果它没有正确实现相关标准规则,这可能不适用于您的编译器,但它仍然很有趣,并且在编译时完全透明地工作:

template<typename>struct Type{};

template<int A>
struct MagicStruct
{
    friend constexpr int get_injected(Type<MagicStruct<A>>);
    static constexpr int current() { return A; }

    template <int V = get_injected(Type<MagicStruct<A>>{})> 
    static constexpr int injected() { return V; } 
};


template<int A, int B>
struct InjectionStruct
{
    static constexpr int first() { return A; }
    static constexpr int second() { return B; }

    friend constexpr int get_injected(Type<MagicStruct<A>>) { return B; }
};

您对此实现的使用几乎完全符合您的要求:

static const int AVALUE = 1;
static const int BVALUE = 2;

static const int CVALUE = InjectionStruct<AVALUE, BVALUE>::first(); //== 1
static const int DVALUE = InjectionStruct<AVALUE, BVALUE>::second(); //== 2
static const int EVALUE = MagicStruct<AVALUE>::injected(); //== 2

Live demo

【讨论】:

  • mate 这种技术很棒,我已经尝试过您之前发布的链接,它似乎有效。您现在已经删除了 f 函数,所以我会再试一次.. 非常感谢!
  • 非常好的解决方案,但不确定是编译时间。
  • 是的,我想做更多的测试.. 那个静态部分看起来很吓人 ;)
  • @TartanLlama 它也适用于这个解决方案,但与这个解决方案相比,第一个似乎更有用。不知道有没有办法封装:template InjectionStruct::filler InjectionStruct::inject;在返回第一个值的通用函数内?
  • 不错的解决方案,但注入不是编译时间值。还要注意 InjectionStructs 的初始化顺序。
【解决方案2】:

以下内容对您有帮助吗?

template<int A, int B>
struct InjectionStruct
{
    constexpr static int first = A;
    constexpr static int second = B;
    constexpr static int MAGIC = B;
};

template<int A, class Injection>
struct MagicStruct
{
    constexpr static int current = A;
    constexpr static auto injected = Injection::MAGIC;
};
static const int AVALUE = 1;
static const int BVALUE = 2;

static const int CVALUE = InjectionStruct<AVALUE, BVALUE>::first; //== 1
static const int CVALUE = InjectionStruct<AVALUE, BVALUE>::second; //== 2
static const int DVALUE = MagicStruct<AVALUE, InjectionStruct<AVALUE, BVALUE>>::injected; //== 2

在不知道你想注入什么的情况下,很难给出更深入的建议。

【讨论】:

  • 也谢谢你 :) 请检查我编辑的帖子。在我的情况下,我只想通过使用 AVALUE 来获得 DVALUE。我使用 MagicStruct 时不知道 BVALUE
【解决方案3】:

您需要在InjectionStructMagicStruct 之间建立某种关系。出于这个原因,我在InjectionStruct 中包含了injected 值和typdef 的附加模板参数。

第一个例子使用InjectionStruct本身来定义具体的MagicStruct:

例子:

// MagicStruct is generic
template<int A, int INJ>
struct MagicStruct
{
    enum { current = A, injected = INJ};

};

// Injection struct has it own copy of MagicStruct
template<int A, int B>
struct InjectionStruct
{
    enum { first=A, second=B};

    // Provide custom magic number
    constexpr static int provideInjection(){ return B;}

    // Own copy of magicStruct, injected by this template instance.
    template <int X> struct MagicStruct: public ::MagicStruct<X,provideInjection()>{};
};

constexpr static const int AVALUE = 1;
constexpr static const int BVALUE = 2;

constexpr static const int CVALUE1 = InjectionStruct<AVALUE, BVALUE>::first; //== 1
constexpr static const int CVALUE2 = InjectionStruct<AVALUE, BVALUE>::second; //== 2
// Using a specific InjectionStruct, provies the MagicStruct with one argument.
constexpr static const int DVALUE3 = InjectionStruct<AVALUE, BVALUE>::MagicStruct<AVALUE>::injected; //== 2


int main()
{
    std::cout << CVALUE1 << std::endl;
    std::cout << CVALUE2 << std::endl;
    std::cout << DVALUE3 << std::endl;

    return 0;
}

在以下解决方案中,MagicStruct 从默认的InjectionStruct 中获取魔法值,这是修复的。

// InjectionStruct is generic.
template<int A, int B>
struct InjectionStruct
{
    enum { first=A, second=B};
    constexpr static int provideInjection(){ return A+B;}
};


constexpr static const int AVALUE = 1;
constexpr static const int BVALUE = 2;

// Define MagicStruct based on a specific InjectionStruct
template<int A, int INJ = InjectionStruct<A,BVALUE>::provideInjection()>
struct MagicStruct
{
    enum { current = A, injected = INJ};
};

constexpr static const int CVALUE1 = InjectionStruct<AVALUE, BVALUE>::first; //== 1
constexpr static const int CVALUE2 = InjectionStruct<AVALUE, BVALUE>::second; //== 2
constexpr static const int DVALUE1 = MagicStruct<3>::injected; // == 5
constexpr static const int DVALUE2 = MagicStruct<DVALUE1>::injected; // == 7
constexpr static const int DVALUE3 = MagicStruct<DVALUE2>::injected; // == 9


int main(int n, char* args[])
{
    std::cout << CVALUE1 << std::endl;
    std::cout << CVALUE2 << std::endl;
    std::cout << DVALUE1 << std::endl;
    std::cout << DVALUE2 << std::endl;
    std::cout << DVALUE3 << std::endl;

    return 0;
}

【讨论】:

  • 感谢您的尝试 :) 检查我编辑的帖子。就我而言,我只想通过使用 AVALUE 来获得 DVALUE。
  • DVALUE 本质上是 BVALUE 的别名,因此它始终依赖于 BVALUE。我认为您想要的是编译器查看 InjectionStruct 的所有定义并根据 AVALUE 选择正确的定义。这种行为是不可能的,即使它会,你也不想这样做,因为它不是一对一的映射。很抱歉这么说,但您应该考虑不同的设计。
  • 我只是在这里做实验,所以没有设计问题。我只是想要一种在模板 MagicStruct 中获取编译时间值的方法,该值已插入而不是使用该模板值。
  • 谢谢 :) 是的,第二个有效。不幸的是,对于我的想法来说,它还不够通用。你必须每次都定义 struct MagicStruct。无论如何都是好的解决方案
  • @user3770392:请解释一下“你必须每次都定义 struct MagicStruct”。
猜你喜欢
  • 2017-11-19
  • 2022-09-28
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
  • 2022-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多