c++ 如何避免对非构造函数进行隐式转换?

nvbavucw  于 2023-03-14  发布在  其他
关注(0)|答案(9)|浏览(126)

如何避免对非构造函数进行隐式强制转换?
我有一个以整数为参数的函数,
但是这个函数也会接受字符、布尔值和长整型。
我相信它是通过隐式地强制转换它们来做到这一点的。
如何避免这种情况,使函数只接受匹配类型的参数,否则将拒绝编译?
有一个关键字“explicit”,但它不适用于非构造函数。:
我该怎么办?
下面的程序将编译,尽管我不希望它编译:

#include <cstdlib>

//the function signature requires an int
void function(int i);

int main(){

    int i{5};
    function(i); //<- this is acceptable

    char c{'a'};
    function(c); //<- I would NOT like this to compile

    return EXIT_SUCCESS;
}

void function(int i){return;}
  • 请务必指出任何术语和假设的误用
js81xvg6

js81xvg61#

定义与所有其他类型匹配的函数模板:

void function(int); // this will be selected for int only

template <class T>
void function(T) = delete; // C++11

这是因为具有直接匹配的非模板函数总是首先被考虑,然后才考虑具有直接匹配的函数模板--所以永远不会使用function<int>,但是对于其他任何函数,比如char,都会使用function<char>--这会导致编译错误:

void function(int) {}

template <class T>
void function(T) = delete; // C++11 

int main() {
   function(1);
   function(char(1)); // line 12
}

错误:

prog.cpp: In function 'int main()':
prog.cpp:4:6: error: deleted function 'void function(T) [with T = char]'
prog.cpp:12:20: error: used here

这是C++03的方法:

// because this ugly code will give you compilation error for all other types
class DeleteOverload
{
private:
    DeleteOverload(void*);
};

template <class T>
void function(T a, DeleteOverload = 0);

void function(int a)
{}
zyfwsgd6

zyfwsgd62#

不能直接使用,因为char会自动升级为int
不过,您可以使用一个技巧:创建一个以char为参数的函数,但不实现它。它可以编译,但会得到一个链接器错误:

void function(int i) 
{
}
void function(char i);
//or, in C++11
void function(char i) = delete;

使用char参数调用函数将中断生成。
参见http://ideone.com/2SRdM
术语:非构造函数?你是指不是构造函数的函数吗?

hiz5n14c

hiz5n14c3#

8年后(PRE-C++20,参见编辑):

如果您不介意模板函数(您可能会介意),最现代的解决方案是使用std::enable_ifstd::is_same的模板函数。
即:

// Where we want to only take int
template <class T, std::enable_if_t<std::is_same_v<T,int>,bool> = false>
void func(T x) {
    
}

编辑(c++20)

我最近转向了c20,我相信有一个更好的方法。**如果你的团队或你不使用c20,或者不熟悉新的概念库,不要使用这个。**这是更好的,并且是新的c++20标准和新特性的作者所概述的预期方法(阅读Bjarne Stroustrup here所写的论文。

template <class T>
    requires std::same_as(T,int)
void func(T x) {
    //...
}

小编辑(概念的不同模式)

下面是一个更好的方法,因为它解释了你的理由,有一个显式的int。如果你经常这样做,并希望有一个好的模式,我会这样做:

template <class T>
concept explicit_int = std::same_as<T,int>;

template <explicit_int T>
void func(T x) {

}

小编2(最后我承诺)

实现这种可能性的另一种方法是:

template <class T>
concept explicit_int = std::same_as<T,int>;

void func(explicit_int auto x) {

}
brccelvz

brccelvz4#

下面是一个通用的解决方案,如果使用int以外的任何值调用function,则会在编译时导致错误

template <typename T>
struct is_int { static const bool value = false; };

template <>
struct is_int<int> { static const bool value = true; };

template <typename T>
void function(T i) {
  static_assert(is_int<T>::value, "argument is not int");
  return;
}

int main() {
  int i = 5;
  char c = 'a';

  function(i);
  //function(c);

  return 0;
}

它的工作原理是允许任何类型的参数起作用,但使用is_int作为类型级 predicate 。is_int的泛型实现有一个false值,但int类型的显式特化有一个true值,以便静态Assert保证参数完全具有int类型,否则会出现编译错误。

u5rb5r59

u5rb5r595#

也许你可以使用一个结构体来使第二个函数私有化:

#include <cstdlib>

struct NoCast {
    static void function(int i);
  private:
    static void function(char c);
};

int main(){

    int i(5);
    NoCast::function(i); //<- this is acceptable

    char c('a');
    NoCast::function(c); //<- Error

    return EXIT_SUCCESS;
}

void NoCast::function(int i){return;}

这将无法编译:

prog.cpp: In function ‘int main()’:
prog.cpp:7: error: ‘static void NoCast::function(char)’ is private
prog.cpp:16: error: within this context
sg2wtvxw

sg2wtvxw6#

对于C14(我相信是C11),你也可以通过重载右值引用来禁用复制构造函数:
例如:假设你有一个Binding<C>基类,其中CConstraint基类,或者是继承类。假设你在一个向量中按值存储Binding<C>,你传递了一个引用给绑定,并且你希望确保你不会导致隐式复制。
对于右值引用特定的情况,可以通过删除func(Binding<C>&& x)(按照PiotrNycz的示例)来实现。
片段:

template<typename T>
void overload_info(const T& x) {
  cout << "overload: " << "const " << name_trait<T>::name() << "&" << endl;
}

template<typename T>
void overload_info(T&& x) {
  cout << "overload: " << name_trait<T>::name() << "&&" << endl;
}

template<typename T>
void disable_implicit_copy(T&& x) = delete;

template<typename T>
void disable_implicit_copy(const T& x) {
  cout << "[valid] ";
  overload_info<T>(x);
}

...

int main() {
  Constraint c;
  LinearConstraint lc(1);

  Binding<Constraint> bc(&c, {});
  Binding<LinearConstraint> blc(&lc, {});

  CALL(overload_info<Binding<Constraint>>(bc));
  CALL(overload_info<Binding<LinearConstraint>>(blc));

  CALL(overload_info<Binding<Constraint>>(blc));

  CALL(disable_implicit_copy<Binding<Constraint>>(bc));
  // // Causes desired error
  // CALL(disable_implicit_copy<Binding<Constraint>>(blc));
}

输出:

>>> overload_info(bc)
overload: T&&

>>> overload_info<Binding<Constraint>>(bc)
overload: const Binding<Constraint>&

>>> overload_info<Binding<LinearConstraint>>(blc)
overload: const Binding<LinearConstraint>&

>>> overload_info<Binding<Constraint>>(blc)
implicit copy: Binding<LinearConstraint>  ->  Binding<Constraint>
overload: Binding<Constraint>&&

>>> disable_implicit_copy<Binding<Constraint>>(bc)
[valid] overload: const Binding<Constraint>&

错误(bazel中的clang-3.9,当未注解有问题的行时):

cpp_quick/prevent_implicit_conversion.cc:116:8: error: call to deleted function 'disable_implicit_copy'
  CALL(disable_implicit_copy<Binding<Constraint>>(blc));
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

完整源代码:防止隐式转换.cc

cnjp1d6j

cnjp1d6j7#

好吧,我打算用下面的代码来回答这个问题,但是即使它可以在Visual C中工作,在产生所需的编译错误的意义上,MinGW g 4.7.1接受它,并调用右值引用构造函数!
我想一定是编译器的错误,但我也可能是错的,所以-有人知道吗?
无论如何,下面是代码,它 * 可能 * 会成为一个符合标准的解决方案(或者,它可能会成为我的一个想法!):

#include <iostream>
#include <utility>      // std::is_same, std::enable_if
using namespace std;

template< class Type >
struct Boxed
{
    Type value;

    template< class Arg >
    Boxed(
        Arg const& v,
        typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
        )
        : value( v )
    {
        wcout << "Generic!" << endl;
    }

    Boxed( Type&& v ): value( move( v ) )
    {
        wcout << "Rvalue!" << endl;
    }
};

void function( Boxed< int > v ) {}

int main()
{
    int i = 5;
    function( i );  //<- this is acceptable

    char c = 'a';
    function( c );  //<- I would NOT like this to compile
}
xqkwcwgp

xqkwcwgp8#

我首先尝试了PiotrNycz的方法(对于C++03,我被迫在一个项目中使用它),然后我试图找到一个更通用的方法,并提出了这个ForcedType<T>模板类。

template <typename T>
struct ForcedType {
    ForcedType(T v): m_v(v) {}
    operator T&() { return m_v; }
    operator const T&() const { return m_v; }

private:
    template <typename T2>
    ForcedType(T2);

    T m_v;
};

template <typename T>
struct ForcedType<const T&> {
    ForcedType(const T& v): m_v(v) {}
    operator const T&() const { return m_v; }

private:
    template <typename T2>
    ForcedType(const T2&);

    const T& m_v;
};

template <typename T>
struct ForcedType<T&> {
    ForcedType(T& v): m_v(v) {}
    operator T&() { return m_v; }
    operator const T&() const { return m_v; }

private:
    template <typename T2>
    ForcedType(T2&);

    T& m_v;
};

如果我没记错的话,这三种特殊化应该涵盖所有常见用例,我不确定是否真的需要对右值引用进行特殊化(C++11以上),或者按值特殊化就足够了。
如果一个函数有3个参数,而第3个参数不允许隐式转换,则可以这样使用它:

function(ParamType1 param1, ParamType2 param2, ForcedType<ParamType3> param3);
ibps3vxo

ibps3vxo9#

这里有一个在C++11中使用enable_if的干净简单的解决方案。这个例子使用std::is_same作为 predicate ,但是你可以使用任何其他产生bool的constexpr。

#include <type_traits> // type_traits has enable_if

// take will only accept double, whereas 'normal' functions
// taking double would also accept e.g. float arguments

// compile this with clang++ to get:
// $ clang++ limit*.cc
// limit_argtypes.cc:16:3: error: no matching function for call to 'take'
//   take ( 1.0f ) ;
//   ^~~~
// limit_argtypes.cc:10:6: note: candidate template ignored: requirement
// 'std::is_same<float, double>::value' was not satisfied
// [with arg_t = float]
// void take ( arg_t rhs )
//      ^
// 1 error generated.

// The error message is even clear and concise.

template < typename arg_t ,
           typename = typename std::enable_if
                      < std::is_same < arg_t , double > :: value
                      > :: type
          >
void take ( arg_t rhs )
{ }

int main ( int argc , char * argv[] )
{
  take ( 1.0 ) ;
  take ( 1.0f ) ;
}

相关问题