【问题标题】:How can I use C++ template argument to decide which type of member is in a class如何使用 C++ 模板参数来确定类中的成员类型
【发布时间】:2018-04-06 23:21:13
【问题描述】:

我想创建一个 Vertex 类,并希望通过能够创建 32 位浮点和 64 位双精度版本以及可能的整数版本来对其进行通用化。我想这样做:

template <typename P>
struct Vertex
{
    if (typeid(P) == typeid(float))
    {
         vec3 position;
         vec3 normal;
         vec2 texcoords;
    }
    else if (typeid(P) == typeid(double))
    {
         dvec3 position; // This is a double vector
         dvec3 normal;
         dvec2 texcoords;
    }
    else if (typeid(P) == typeid(int))
    {
         ivec3 position; // This is an integer vector
         ivec3 normal;
         ivec2 texcoords;
    }

};

我不认为 if 语句不会在编译时评估,所以这只是我想做的一个说明。有没有办法做到这一点?还是我必须专门针对每种类型,还是只重写所有不同的版本?

【问题讨论】:

  • @frslm 那篇文章是关于检查函数体的,这可能与类的定义不同。类定义完全依赖于编译时,而函数体可以在运行时进行评估。
  • 最简单的解决方案可能是使用std::conditional
  • 你不想专攻,因为?
  • 这里最重要的一点:请使用Eigen。它是一个 C++ 线性代数库,提供模板化向量(和矩阵),它也是为您的用例而设计的。

标签: c++ class templates


【解决方案1】:

您可能想要某种vec3vec2 选择器类型。如果已经有 vec3vec2 的模板版本,只需使用它们。否则,您可以使用模板特化:

template <typename T>
struct vec_selector {};

template <>
struct vec_selector<float> {
    using vec3_type = vec3;
    using vec2_type = vec2;
};

template <>
struct vec_selector<double> {
    using vec3_type = dvec3;
    using vec2_type = dvec2;
};

template <>
struct vec_selector<int> {
    using vec3_type = ivec3;
    using vec2_type = ivec2;
};

template <typename P>
using vec3_select_t = typename vec_selector<P>::vec3_type;

template <typename P>
using vec2_select_t = typename vec_selector<P>::vec2_type;

那么你可以简单地写:

template <typename P>
struct Vertex
{
    vec3_select_t<P> position;
    vec3_select_t<P> normal;
    vec2_select_t<P> texcoords;
};

您也可以只专门化 Vertex 模板,但在别处使用 vec3_select_t 似乎很有用,并且您必须在 Vertex 上重复任何成员函数(或者编写代码更复杂)

【讨论】:

  • @Zebrafish - 这个想法是你认为 C++ 强大的一切的背后。
  • @StoryTeller 你认为 D 或 Rust 有机会超越 C++,成为更流行的低级语言吗?我的意思是在多年后设计某些东西时,我们有经验不会在设计中重复我们的错误或愚蠢的选择,这样我认为他们应该有潜力变得“更好”,当然如果做得明智的话。
  • @Zebrafish 总有机会,但我不指望它。如果它们最终确实变得比 C++ 更受欢迎,那将是相当遥远的未来。我个人认为公司会更愿意花钱改进 C++ 而不是取代 C++,因为 C++ 已经有了很大的领先优势
  • @Zebrafish - IDK,我也不熟悉。此外,从历史上看,模板机制的强大是一种巧合,而不是有意识的设计选择。
  • @Zebrafish - 顺便说一句,我从你的第一条评论中得知这对你有用。那你应该接受答案。这将是很好的网络礼仪。
【解决方案2】:

这是另一种选择

template<typename T>
struct Type { typedef T type; };

template<typename T>
inline constexpr Type<T> type{};

template <typename P>
struct Vertex
{
    static constexpr auto D3 = []{ 
        if constexpr(std::is_same_v<P,float>)
            return type<vec3>;
        else if constexpr(std::is_same_v<P,double>)
            return type<dvec3>;
        else if constexpr(std::is_same_v<P,int>)
            return type<ivec3>;
    }();

    static constexpr auto D2 = []{ 
        if constexpr(std::is_same_v<P,float>)
            return type<vec2>;
        else if constexpr(std::is_same_v<P,double>)
            return type<dvec2>;
        else if constexpr(std::is_same_v<P,int>)
            return type<ivec2>;
    }();

    typename decltype(D3)::type position;
    typename decltype(D3)::type normal;
    typename decltype(D2)::type texcoords;
};

Type 模板上稍加努力,您就可以对 lambdas 的代码进行相当多的改进(也许您已经看过 boost hana,它也遵循了这个想法)

template<typename T>
struct Type {
   typedef T type;   
   friend constexpr bool operator==(Type, Type) {
       return true;
   }
};

template<typename T1, typename T2>
constexpr bool operator==(Type<T1>, Type<T2>) {
    return false;
}

template<typename T>
inline constexpr Type<T> type{};

现在它不再需要std::is_same_v

template <typename P>
struct Vertex
{
    static constexpr auto D3 = [](auto t) { 
        if constexpr(t == type<float>)
            return type<vec3>;
        else if constexpr(t == type<double>)
            return type<dvec3>;
        else if constexpr(t == type<int>)
            return type<ivec3>;
    }(type<P>);

    static constexpr auto D2 = [](auto t) { 
        if constexpr(t == type<float>)
            return type<vec2>;
        else if constexpr(t == type<double>)
            return type<dvec2>;
        else if constexpr(t == type<int>)
            return type<ivec2>;
    }(type<P>);

    typename decltype(D3)::type position;
    typename decltype(D3)::type normal;
    typename decltype(D2)::type texcoords;
};

丑陋的decltype 写作也可以通过使用auto 来修复

template<auto &t>
using type_of = typename std::remove_reference_t<decltype(t)>::type;

所以你可以写

type_of<D3> position;
type_of<D3> normal;
type_of<D2> texcoords;

【讨论】:

  • 为什么需要Choose?在我看来它应该在没有它的情况下工作(并且没有三元组)
  • @Justin lambdas 不是 constexpr (据我所知),所以你不能直接把它作为初始化程序。此处解释了选择器技巧:artima.com/cppsource/foreach.html
  • 认为它们应该是 C++17 中的 constexpr
  • @Justin 啊,你是对的!好吧,它没有为我编译。也许编译器还不支持它,或者我犯了一些错误。所以我猜你实际上可以删除所有的选择噪音。
  • @Justin 我重新测试了它,GCC / Clang 在没有选择的情况下接受它。谢谢你的提问。我从答案中删除了令人困惑的技巧。
【解决方案3】:

OP 在 cmets 中提到他们正在使用 GML。

GLM 向量实际上是模板,因此不需要复杂的解决方案:

template <typename P>
struct Vertex
{
     tvec3<P> position;
     tvec3<P> normal;
     tvec2<P> texcoords;
};

【讨论】:

  • 你完全正确。哎呀,我觉得很傻。我没有意识到您可以自己实例化模板类,因为我只使用了 vec3、vec4 等。它们的类型定义为类似于 highp_vec3,它的类型定义为 typedef tvec3。谢谢。
【解决方案4】:

这只是贾斯汀解决方案的补充。无论如何,这实际上是他的。使用 std::conditional 的想法是 François Andrieux 在 cmets 中给出的想法。大致如下:

template <typename P>
struct vertex
{
    using vec3_t = std::conditional_t <std::is_same_v<float, P>,
        /*IF FLOAT*/     vec3,
        /*OTHERWISE*/    std::conditional_t <is_same_v<double, P>,
        /*IF DOUBLE*/    dvec3,
        /*IF INT*/       ivec3>>;

    vec3_t position;
    vec3_t normal;
    //vec2_t texcoords; WILL HAVE TO TYPEDEF THIS AS WELL.
};

Johannes Schaub 在 cmets 中也给出了基于 constexpr 和 decltype 的两种不同解决方案。

【讨论】:

  • 如果你使用std::is_same_v,那是C++17,所以你可以用std::conditional_t&lt;...&gt;(那是C++14)替换typename std::conditional&lt;..&gt;::type。我还建议使用 using 来作为类型别名,但这可能取决于个人喜好
  • @贾斯汀很好。我也尝试删除 std:: ,但似乎在结构中不允许“使用命名空间”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 2021-01-04
  • 1970-01-01
相关资源
最近更新 更多