【问题标题】:Specialize auto trailing return type专门化自动尾随返回类型
【发布时间】:2015-07-21 10:44:45
【问题描述】:

我有一些代码使用两种不同类型的颜色,每通道 8 位和每通道 16 位,每种颜色都由一个结构表示。为了有效地重用我的代码,我有一个模板函数可以对它们进行一些渲染。因此,我想要一个模板函数来获取我的颜色通道的最大值。

我最初的尝试是这样的。我只展示了 8 bpc 的专业化

struct Pixel8
{
   unsigned char r;
   unsigned char g;
   unsigned char b;
};
#define PIXEL8_MAX 255

template <class PIXEL>
auto getMax( ) -> decltype( PIXEL::r )
{
   static_assert( sizeof(PIXEL) > 0, "getMax can only be called with a pixel type." );
}
template <>
auto getMax<Pixel8>( ) -> decltype( Pixel8::r )
{
   return PIXEL8_MAX;
}

这不能用 Visual Studio 2012 编译。我收到错误

1> 错误 C2785: ''unknown-type' getMax(void)' 和 'char getMax(void)' 有不同的返回类型 1> 查看​​'getMax'的声明

对我来说,我觉得这应该可行,但我找不到任何示例。 Specialize function template with decltype trailing return type 上还有一个类似的问题,但这里每个专业化的返回类型都是相同的。

我找到了一种解决方法,我将把它作为答案发布,以便其他人可以受益。但是它不是很透明,所以如果有人可以告诉我上面的代码是否有效并且这是 VC++ 不兼容,或者如果它无效,那么为什么以及如何使它有效?

【问题讨论】:

  • 顺便说一句,255 不适合 signed char(但适合 unsigned char)。 char 可以签名。
  • static_assert 总是会在某些编译器上触发,因为它不依赖于模板参数。
  • 我认为您应该查看traits 而不是普通函数。
  • @Jarod42,谢谢我已经编辑了问题
  • @TartanLlama 好的,我没有意识到这一点。我猜那是因为编译器看不到对模板的依赖,所以将它编译为非模板函数或什么?我已经编辑添加了 sizeof(T) >0 ,我在其他地方看到过,但没有意识到需要。

标签: c++ templates


【解决方案1】:

尽量使返回类型依赖于模板参数类型:

struct Pixel8
{
    char r;
    char g;
    char b;
};

template<typename T>
struct ColourType
{
    typedef decltype(T::r) type;
};

#define PIXEL8_MAX 255

template <class PIXEL>
typename ColourType<PIXEL>::type getMax()
{
    static_assert(false, "getMax can only be called with a pixel type.");
}
template <>
ColourType<Pixel8>::type getMax<Pixel8>()
{
    return PIXEL8_MAX;
}

【讨论】:

  • 这就像我的解决方案,但更好。它通过在结构中使用 decltype 来缩短它,从而避免专门化它并且不使用尾随返回类型。有趣的是,尾随和非尾随返回类型似乎完全相同。可以以一种方式声明特化,而以另一种方式声明基本模板函数,它仍然可以在 VC++ 2012 上编译。
【解决方案2】:

这是获得所需行为的解决方法。它依赖于这样一个事实,即有另一种方法可以在不使用 decltype 的情况下在 C++ 中定义类型。这是使用 typname。

它在这个上下文中的主要用途如下,想象两个类和两个函数

class MyClass
{
public:
    class MyThing
    {
    };
};

class MyOtherClass
{
public:
    static int MyThing;
}

template< class T >
void func1( T something )
{
    typename T::MyThing thing;
}

template< class T >
void func2( T something )
{
    T::MyThing = 5;
}

如果我们将任一类作为模板参数 T 传递,那么 T::MyThing 将是 MyClass 的类型和 MyOtherClass 的静态 int。这些是完全不兼容的,所以我们使用 typename 来分隔它们。 在 func1 中,我们使用 typename 来声明 T::MyThing 是一个类型。我们可以传入一个 MyClass 对象。在 func2 中我们省略了 typename,因此 T::MyThing 被解释为一个变量,我们可以传入一个 MyOtherClass。如果没有 typename,就无法判断 T::MyThing 是类型还是静态变量。

还要注意 typename 可以引用 typdef 以及内部类,所以如果我们创建一个包含 typedef 的模板类或结构,我们可以使用 typename 访问该类型。

template<class PIXEL>
struct pixTypes
{
};

template<>
struct pixTypes<Pixel8>
{
    typedef char type;
};

template <class PIXEL>
auto getMax( ) -> typename pixTypes<PIXEL>::type
{
    static_assert( false, "getMax can only be called with a pixel type." );
}
template <>
auto getMax<Pixel8>() -> typename pixTypes<Pixel8>::type
{
    return PIXEL8_MAX;
}

所以现在我们从一个类型名中获取我们的返回类型,该类型名引用一个专门的模板化结构中的 typedef。

这似乎是一种复杂的方式,但它确实可以在 Visual Studio 12 上编译。

【讨论】:

    【解决方案3】:

    使用宏来定义常量,就像这个问题的代码一样

    #define PIXEL8_MAX 255
    

    ……并不理想。

    此外,定义与使用的类型冲突。 char 不保证具有该最大值,并且大多数实现默认情况下没有该最大值。您可以将Byte 类型定义为unsigned char,但即使这样也不能保证 8 位,应该检查一下。

    标准库提供了numeric_limits类模板来处理最大值等:

    #include <limits>       // std::numeric_limits
    
    #define STATIC_ASSERT( e ) static_assert( e, #e )
    
    using Byte = unsigned char;
    int const bits_per_byte = std::numeric_limits<Byte>::digits;
    STATIC_ASSERT( bits_per_byte == 8 );
    
    struct Pixel8
    {
        Byte r;
        Byte g;
        Byte b;
    };
    
    template< class Pixel >
    constexpr auto getMax() -> decltype( Pixel::r )
    {
        return std::numeric_limits<decltype( Pixel::r )>::max();
    }
    
    #include <iostream>
    using namespace std;
    
    auto main() -> int
    {
        cout << +getMax<Pixel8>() << endl;
    }
    

    【讨论】:

    • 谢谢,您说#define 并不理想是正确的。我使用它是因为实际上我使用的是库的像素结构,而他们使用了#defines。一个 const unsigned char 会更好。
    • 我已将 char 编辑为 unsigned char,这是我的错误。尽管您对 8 位无符号字符表示严格要求是正确的,但我使用的(商业)库并非如此。它使用类型定义的无符号字符。我猜几乎每个实现都有一个 8 位字符。
    • 我认为这是在 GCC 上编译的? VC++ 2012 不支持 costexpr,虽然我刚刚看过并且(非常)最近发布的 VC++ 2015 似乎确实支持。
    • 最后,无论我的元素的实际大小如何,我都需要返回一个给定的值。该库的 16 位颜色实际上只使用了 2^15 个值。
    • 为什么不专门针对这种特殊情况?
    猜你喜欢
    • 2022-11-30
    • 2015-09-10
    • 1970-01-01
    • 2018-08-29
    • 2018-05-12
    • 2011-11-07
    • 2017-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多