在编译时使用模板验证类型

时间:2016-07-03 18:55:00

标签: c++ c++98

如果我将float号码传递给我的班级,并希望在下面的代码中使用int,我希望收到编译错误。以下代码使用gcc 4.9.3

运行
gcc -std=c++98 -O0 -g3 -Wall -Wextra -c -fmessage-length=0

如果类实例类型和方法输入参数类型之间的类型不同,我应该如何更改它将返回编译错误的代码?

#include <iostream>
template <typename T>
class CheckValidity {
    public:
        CheckValidity(){};

        T isFoo(T variable)
        {
            T result = static_cast<T>(0);
            if ( variable > static_cast<T>(5) )
            {
                result = static_cast<T>(1);
            }
            return result;
        };
};

int main() {
    CheckValidity<int> checkValidityInt;
    std::cout << checkValidityInt.isFoo(6.0f) << std::endl;
    return 0;
}

6 个答案:

答案 0 :(得分:2)

您现在没有收到警告的原因是因为float6.0f可以无损地转换为int。也就是说,在转换过程中你不会失去任何精度,因为6.0可以精确地表示为6.编译器知道这一点,因为你使用的是编译时常量。

例如,如果您将其更改为6.1f,则会收到警告(假设您已启用-Wconversion):

int main() {
    CheckValidity<int> checkValidityInt;
    std::cout << checkValidityInt.isFoo(6.1f) << std::endl;
    return 0;
}
In function 'int main()':  
20 : warning: conversion to 'int' alters 'float' constant value [-Wfloat-conversion]  
std::cout << checkValidityInt.isFoo(6.1f) << std::endl;

如果您传递的是float类型的变量,而不是常量,您也会收到警告。这意味着警告应该捕获任何潜在的问题。精确转换常数不是一个真正的问题。

要将此警告转换为错误(确保您不会错过),也请传递-Werror标记。

答案 1 :(得分:2)

对于c ++ 98:

实现您想要完全匹配的一种方法是使用模板方法接受任何类型但由于SFINAE而总是失败:

    template<typename U>
    U isFoo(U variable)
    {
      typename CheckValidity::InvalidType x;
    }

对于c ++ 11:

您可以使用已删除的方法更干净地实现相同的效果:

    template<typename U>
    U isFoo(U variable) = delete;

答案 2 :(得分:0)

使成员函数成为模板成员函数。它开辟了处理不同类型的几种可能性。

使用static_assertstd::is_floating_point确保不使用浮点类型调用该函数。

template <typename U>
T isFoo(U variable)
{
    static_assert(std::is_floating_point<U>::value == false,
                  "Can't handle floating point types");
    T result = static_cast<T>(0);
    if ( variable > static_cast<T>(5) )
    {
        result = static_cast<T>(1);
    }
    return result;
};

要确保UT属于同一类型,您可以使用std::is_same

template <typename U>
T isFoo(U variable)
{
    static_assert(std::is_same<T, U>::value,
                  "Can't handle different types");
    T result = static_cast<T>(0);
    if ( variable > static_cast<T>(5) )
    {
        result = static_cast<T>(1);
    }
    return result;
};

为了能够处理U T的派生类型,您可以使用:

template <typename U>
T isFoo(U variable)
{
    static_assert(std::is_base_of<T, U>::value,
                  "Can't handle incompatible types");
    T result = static_cast<T>(0);
    if ( variable > static_cast<T>(5) )
    {
        result = static_cast<T>(1);
    }
    return result;
};

答案 3 :(得分:0)

您可以使用模板专业化:

//Returns false in the default case
template<typename V>
bool isFoo(V)
{
    return false;
};

//Returns true if the type passed is T
template<>
bool isFoo<T>(T)
{
    return true;
}

变量仍然作为没有名称的参数传递,因为你不需要在内部使用它。它仍然使用,因为isFoo<decltype(var)>()优于isFoo(var)

<小时/> 如果您可以使用C ++ 11,请使用std::is_same或类似的结构:

//You need a separate type, because if you use T, you will just cast the result to T
template<typename V>
bool isFoo(V)
{
    //Compare the types
    return std::is_same<V, T>::value;
};

答案 4 :(得分:0)

如果您只是声明公共模板类,但未定义并专门为float设置模板类定义,则可以更好地实现此效果。我会尝试玩类型列表......

答案 5 :(得分:0)

听起来您希望将std::is_integralstatic_assert结合使用。

在要限制为整数的任何函数或类模板中,可以调用:

static_assert(std::is_integral<T>::value, "Integer required.");

您可能也对std::enable_if(或者便捷typedef enable_if_t)感兴趣:

template <typename Integer,
          typename = std::enable_if_t<std::is_integral<Integer>::value>>

如果表达式为false,则可以将调用static_assert与任何整型常量表达式一起使用以生成编译时错误。您可以使用enable_if来限制可以实例化模板的类型(如果使用无效类型,也会导致编译器错误)。

编辑:我刚注意到你正在使用-std = c ++ 98。如果您需要使用该标准,我可以删除此答案。但是如果你可以使用C ++ 11,这将比你在早期标准中所能做的要好。