【问题标题】:Ensuring template type consistency at compile-time在编译时确保模板类型的一致性
【发布时间】:2011-01-24 16:20:49
【问题描述】:

我有这个模板类:

template <class Tin, class Tout>
class Foo
{
    Tin input;
    Tout output;

    static inline void __ensure_type_consistency
    {
        int16_t* p = (int16_t *)0;
        // uint16_t* p1 = p;
        Tin* check_type_in = p;
        Tout* check_type_out = p;  
    }
public:
    ...
}

我想确保 TinTout 都是 typedef 类型以键入 int16_t 而不是其他类型。 (注意: 请在下结论之前阅读完整的问题

如果我取消注释注释行,我会得到预期的错误;编译器不允许不同类型的指针在没有强制转换的情况下相互分配:

"src\foo.h", line 47: error #145: a value of type "int16_t *" 
cannot be used to initialize an entity of type "uint16_t *"

但如果我把它注释掉,我会实例化:

Foo<uint16_t, int32_t> illegalFoo;

即使使用相同类型的检查,我也没有收到编译器错误(在从未实际调用的静态函数中创建不兼容的指针分配,但这会导致编译器错误)

有没有办法创建静态编译时类型一致性检查?为什么我使用的那个不起作用?

注意:暂时忽略摆脱模板参数的明显解决方案。这将“解决”这个问题,但是我的调试工具在这里发生了一些带外的事情,其中​​ typedef 被用于传递重要的元数据:我想确保 Foo.input 的类型为 Tin Tinint16_t 或解析为 int16_t 的 typedef,与 Foo.outputTout 类似。从我的调试工具的角度来看,存在细微差别,可以区分 typedef 的类型和它们的基类型,即使在 C++ 程序中它们是相同的。


编辑:顺便说一句,这是一个嵌入式系统,我不能使用 Boost。它也不是 C++0x。

【问题讨论】:

  • 啊!双下划线名称 == 为编译器保留...
  • 错误消息告诉您,您将 uint16_t 作为参数传递给模板,而不是 int16_t。您要执行的确切一致性检查是什么?是否要同时允许 uint16_tint16_t?这是一个错字吗(提供的代码中还有其他错误)?
  • 为什么不能在嵌入式系统上使用 Boost?我目前正在嵌入式医疗系统上使用它。
  • @GrahamS:这是一个 TI C28xxx DSP。有嵌入式系统,然后有嵌入式系统。功能更强大的通常具有兆字节的 RAM,并且能够运行操作系统(例如嵌入式 Linux 或智能手机)。我与那些有
  • Foo 只是在 __ensure_type_consistency() 上给出了替换失败,所以它没有被实例化。在 Foo 上没有替换失败,所以它被编译了,然后你在 "uint16_t* p1 = p;" 上得到编译器错误

标签: c++ templates typedef


【解决方案1】:

您可以在static_assert 中使用is_same 类型特征。 C++0x 看起来像:

static_assert(std::is_same<Tin, std::int16_t>::value &&
              std::is_same<Tout, std::int16_t>::value, "o noez");

您也可以在 Boost 中找到 the is_same type traitstatic assert。即使您正在为嵌入式系统进行编译,也可以直接从 Boost 中提取类型特征和静态断言标头,并且这些库都没有任何运行时开销。

【讨论】:

    【解决方案2】:

    一种简单的方法:

    template <class, class> class Foo;
    
    template <>
    class Foo<int16_t, int16_t> {
      ...
    };
    

    如果您的条件实际上更复杂,另一种方法是使用 Boost 中的 BOOST_STATIC_ASSERT 或 C++0x 中的 static_assert。由于您想要非 Boost 选项并且 C++0x 可能无法正常工作,因此这里的内容与您在示例中发布的内容更相似:

    (void)(true ? (int16_t**)0 : (Tin**)0);
    

    Tout 也是如此。这需要在某种被调用或获取地址的方法中(这样做的习惯用法在the Boost.Concept_check implementation documentation 的末尾)。如果您想要其他人发布但没有 Boost 或 C++0x 的 is_same/static_assert 解决方案,请尝试:

    template <class, class> struct types_valid {static const int value = -1;};
    template <> struct types_valid<int16_t, int16_t> {static const int value = 1;};
    

    然后将static char foo[types_valid&lt;Tin, Tout&gt;::value]; 放在你的类中,放在任何方法之外。我相信你不需要真正定义foo,只要你不在任何地方引用它。

    【讨论】:

    • 不,这行不通。我需要成员变量是 Tin 和 Tout 类型,而不是 int16_t。 (同样,从 C++ 的角度来看,它们是相同的,但从我的调试工具来看却不是)
    • @Jason S:试试我最近发布的最后一篇文章(使用?:),看看是否满足您的要求。
    • 这似乎也不起作用。我想知道模板类型是否导致编译器忽略其正常的类型检查。
    • 您是否将该代码放入您的方法中,然后尝试调用该方法?如果您不调用该方法,则其内部的类型检查将不会运行。
    • 您必须检查一些实际被调用(或获取其地址)的方法才能被实例化。
    【解决方案3】:

    虽然你不能使用 boost 或 C++1x,但你可以制作自己的 is_same 编译时类型比较器并破解一些穷人的编译时断言

    // Beware, brain-compiled code ahead!
    template< typename T1, typename T2>
    struct is_same      { static const bool result = false; };
    
    template< typename T > 
    struct is_same<T,T> { static const bool result = true; };
    
    template< bool Condition, typename Dummy = void >
    struct static_assert {
      typedef bool result;
    };
    
    template<typename IntentionalError>
    struct static_assert<false,IntentionalError> {
      typedef typename IntentionalError::does_not_exist result;
    };
    

    并像这样使用它:

    template <class Tin, class Tout>
    class Foo
    {
        Tin input;
        Tout output;
    
        typedef typename static_assert<is_same<Tin ,int16_t>::result>::result Tin_test;
        typedef typename static_assert<is_same<Tout,int16_t>::result>::result Tout_test;
        typedef typename static_assert<is_same<Tout,Tout   >::result>::result Tout_test;
    // ...
    };
    

    【讨论】:

    • Arf,打败我:p @Jason:编译器必须检查类型,而仅在使用方法时才检查方法。
    • 有趣。我会试试看。谢谢!
    • 嗯,编译时测试如何?如果测试失败,我想引起编译时错误。
    • @Jason:它可能不起作用(我还没有编译它),但是如果 static_assert&lt;false,..&gt; 是(默认参数) void,因为 void 没有嵌套的 does_not_exist 类型。
    【解决方案4】:

    您的方法有效,但您必须在某处实际调用 __ensure_type_consistency ( ) 以便编译器抛出错误。如果该方法从未被调用,那么编译器认为它可以忽略它。

    我刚刚在 VC++ 2010 中尝试过,它可以工作。


    您在使用 GCC 吗?尝试在 __ensure_type_consistency 上使用 attribute ((used))。

    【讨论】:

      【解决方案5】:

      您可以使用 Boost 概念检查:

      BOOST_CONCEPT_ASSERT((boost::Integer<Tin>));
      BOOST_CONCEPT_ASSERT((boost::Integer<Tout>));
      

      http://www.boost.org/doc/libs/1_45_0/libs/concept_check/using_concept_check.htm

      【讨论】:

        【解决方案6】:

        我已经调整了对这个问题的几个回答,这似乎很好地解决了这个问题:

        辅助类:

        template <typename T1, typename T2>
        struct TypeConsistency
        {
        private:
            static inline void checkfunction()
            {
                T1* p1 = (T1 *)0;
                T2* p2 = p1;
            }
        public:
            static inline void check() { checkfunction; }
            /* The above line does nothing at runtime,
             * but takes the address of checkfunction(),
             * which causes a compile-time type check.
             */ 
        };
        

        在我的 foo 类中应用它:

        template <class Tin, class Tout> 
        class Foo
        {
            Tin input;
            Tout output;
        
        public:
            Foo() { 
                TypeConsistency<int16_t, Tin>::check();
                TypeConsistency<int16_t, Tout>::check();
            }
            ...
        }
        

        【讨论】:

        • 我误读了这个问题,但现在我已经重读了它,我认为使用某种不需要创建函数的静态断言会更好:template &lt;class A,class B&gt; struct is_same { static const bool value = false; }; template &lt;class A&gt; struct is_same&lt;A,A&gt; { static const bool value = true; }; typedef char assert_are_same[ is_same&lt;T1,uint16_t&gt;::value? 1 : -1 ]; 或该行中的某些内容根本不需要编译器生成代码。
        • 请注意,typedef 只是别名,但它们代表单一类型。这意味着 typedef int a; typedef int b; is_same&lt;a,b&gt;::value; 将评估为 true。
        • @dribeas: 有趣...所以typedef char somedummyexpr[-1]; 是什么会导致编译失败?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-09
        • 2021-11-22
        • 2014-03-31
        • 1970-01-01
        • 2012-05-16
        相关资源
        最近更新 更多