55

我知道您可以对类的构造函数使用 C++ 关键字“显式”来防止类型的自动转换。您可以使用相同的命令来防止类方法的参数转换吗?

我有两个班级成员,一个将 bool 作为参数,另一个是 unsigned int。当我用 int 调用函数时,编译器将 param 转换为 bool 并调用了错误的方法。我知道最终我会替换 bool,但现在不想在开发这个新例程时破坏其他例程。

4

8 回答 8

72

不,您不能使用显式,但您可以使用模板函数来捕获不正确的参数类型。

使用C++11,您可以将模板化函数声明为deleted。这是一个简单的例子:

#include <iostream>

struct Thing {
    void Foo(int value) {
        std::cout << "Foo: value" << std::endl;
    }

    template <typename T>
    void Foo(T value) = delete;
};

如果您尝试Thing::Foo使用size_t参数调用,则会出现以下错误消息:

error: use of deleted function
    ‘void Thing::Foo(T) [with T = long unsigned int]’

C++11 之前的代码中,它可以使用未定义的私有函数来完成。

class ClassThatOnlyTakesBoolsAndUIntsAsArguments
{
public:
  // Assume definitions for these exist elsewhere
  void Method(bool arg1);
  void Method(unsigned int arg1);

  // Below just an example showing how to do the same thing with more arguments
  void MethodWithMoreParms(bool arg1, SomeType& arg2);
  void MethodWithMoreParms(unsigned int arg1, SomeType& arg2);

private:
  // You can leave these undefined
  template<typename T>
  void Method(T arg1);

  // Below just an example showing how to do the same thing with more arguments
  template<typename T>
  void MethodWithMoreParms(T arg1, SomeType& arg2);
};

缺点是在这种情况下代码和错误消息不太清楚,因此应尽可能选择 C++11 选项。

对每个采用boolor的方法重复此模式unsigned int。不要为该方法的模板化版本提供实现。

这将强制用户始终显式调用 bool 或 unsigned int 版本。

Method由于成员是私有的,因此任何使用除bool或之外的类型调用的尝试unsigned int都将无法编译,当然,受可见性规则的标准例外(朋友、内部调用等)的约束。如果有访问权限的东西调用了私有方法,你会得到一个链接器错误。

于 2008-10-06T19:56:15.400 回答
14

不可以explicit防止特定类之间的自动转换,而与上下文无关。当然,你不能为内置类做到这一点。

于 2008-10-06T19:17:14.403 回答
8

以下是一个非常基本的包装器,可用于创建强 typedef:

template <typename V, class D> 
class StrongType
{
public:
  inline explicit StrongType(V const &v)
  : m_v(v)
  {}

  inline operator V () const
  {
    return m_v;
  }

private:
  V m_v; // use V as "inner" type
};

class Tag1;
typedef StrongType<int, Tag1> Tag1Type;


void b1 (Tag1Type);

void b2 (int i)
{
  b1 (Tag1Type (i));
  b1 (i);                // Error
}

这种方法的一个很好的特点是,您还可以区分具有相同类型的不同参数。例如,您可能有以下内容:

class WidthTag;
typedef StrongType<int, WidthTag> Width;  
class HeightTag;
typedef StrongType<int, HeightTag> Height;  

void foo (Width width, Height height);

'foo' 的客户会清楚哪个参数是哪个。

于 2008-10-06T21:22:12.357 回答
2

可能对您有用的是使用模板。下面显示了foo<>()专门用于boolunsigned int和的模板函数int。该main()函数显示调用如何得到解决。请注意,使用int未指定类型后缀的常量的调用将解析为,因此如果您不专注于foo<int>(),则会收到错误调用。如果是这种情况,使用文字整数常量的调用者将不得不使用后缀来获取调用来解析(这可能是您想要的行为)。foo( 1)int"U"

否则,您必须专门研究int并使用"U"后缀或将其转换为 an unsigned int,然后再将其传递给unsigned int版本(或者,如果您想要的话,可能会断言该值不是负数)。

#include <stdio.h>

template <typename T>
void foo( T);

template <>
void foo<bool>( bool x)
{
    printf( "foo( bool)\n");
}


template <>
void foo<unsigned int>( unsigned int x)
{
    printf( "foo( unsigned int)\n");
}


template <>
void foo<int>( int x)
{
    printf( "foo( int)\n");
}



int main () 
{
    foo( true);
    foo( false);
    foo( static_cast<unsigned int>( 0));
    foo( 0U);
    foo( 1U);
    foo( 2U);
    foo( 0);
    foo( 1);
    foo( 2);
}
于 2008-10-06T21:04:59.223 回答
1

当前接受的答案(使用私有模板函数)很好,但已过时。在 C++11 中,我们可以使用deleted 函数来代替:

#include <iostream>

struct Thing {
    void Foo(int value) {
        std::cout << "Foo: value" << std::endl;
    }

    template <typename T>
    void Foo(T value) = delete;
};

int main() {
    Thing t;
    int int_value = 1;
    size_t size_t_value = 2;

    t.Foo(int_value);

    // t.Foo(size_t_value);  // fails with below error
    // error: use of deleted function
    //   ‘void Thing::Foo(T) [with T = long unsigned int]’

    return 0;
}

这更直接地传达了源代码的意图,并在尝试使用具有不允许的参数类型的函数时为用户提供更清晰的错误消息。

于 2019-09-20T01:53:18.560 回答
0

编译器给出了“模棱两可的调用”警告,这就足够了。

我正在做 TDD 开发,并没有意识到我忘记在模拟对象中实现相应的调用。

于 2008-10-06T19:05:38.577 回答
0

bool 是一个限制为 0 或 1 的 int。这就是 return 0; 的整个概念,它在逻辑上与 return false; 相同;(尽管不要在代码中使用它)。

于 2008-10-06T19:51:54.567 回答
-1

您还可以编写一个调用 bool 的 int 版本。

于 2008-10-06T19:14:30.897 回答