如何避免对非构造函数进行隐式转换?
我有一个函数,它以整数作为参数,
但是这个功能也会使用字符,bool和longs
我相信它是通过隐式铸造来实现的
如何避免这种情况,以便函数只接受匹配类型的参数,否则将拒绝编译?
有一个关键字“显式”但它不适用于非构造函数。 :\
我该怎么办?
以下程序编译,但我不想这样做:
#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;}
*请务必指出任何滥用术语和假设的行为
答案 0 :(得分:37)
定义与所有其他类型匹配的函数模板:
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)
{}
答案 1 :(得分:18)
您不能直接,因为char
会自动升级为int
。
您可以采用一种技巧:创建一个以char
为参数但不实现它的函数。它会编译,但你会得到一个链接器错误:
void function(int i)
{
}
void function(char i);
//or, in C++11
void function(char i) = delete;
使用char
参数调用该函数将破坏构建。
术语:非构造函数?你的意思是一个不是构造函数的函数吗?
答案 2 :(得分:9)
如果您不介意模板函数(您可能会介意),最现代的解决方案是使用带有 std::enable_if
和 std::is_same
的模板函数。
即:
// Where we want to only take int
template <class T, std::enable_if_t<std::is_same<T,int>,bool> = false>
void func(T x) {
}
我最近切换到 c++20,我相信有更好的方法。 如果您的团队或您不使用 c++20,或者不熟悉新概念库,请不要使用它。这更好,并且作为新 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) {
}
也是实现这种可能性的一种方式:
template <class T>
concept explicit_int = std::same_as<T,int>;
void func(explicit_int auto x) {
}
答案 3 :(得分:5)
这是一个通用解决方案,如果使用除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
作为类型级谓词。 is_int
的泛型实现具有false值,但int类型的显式特化值为true,因此静态断言保证参数具有完全类型int
,否则存在编译错误。
答案 4 :(得分:1)
好吧,我打算用下面的代码回答这个问题,但即使它适用于Visual C ++,在产生所需的编译错误的意义上,MinGW g ++ 4.7.1接受它,并调用rvalue引用构造函数!
我认为它一定是编译器错误,但我可能错了,所以 - 任何人?
无论如何,这里的代码是可能结果是符合标准的解决方案(或者,可能会证明这是我的思想!):
#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
}
答案 5 :(得分:1)
对于C ++ 14(我相信C ++ 11),您也可以通过重载rvalue-references来禁用复制构造函数:
实施例:
假设您有一个基类Binding<C>
类,其中C
是基类Constraint
类,或者是继承类。假设您在向量中按值存储Binding<C>
,并且您传递了对绑定的引用,并且您希望确保不会导致隐式副本。
您可以通过删除func(Binding<C>&& x)
(根据PiotrNycz的例子)来获取rvalue-reference特定情况。
段:
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>&
错误(clang-3.9
中的bazel
,当取消注释违规行时:
cpp_quick/prevent_implicit_conversion.cc:116:8: error: call to deleted function 'disable_implicit_copy'
CALL(disable_implicit_copy<Binding<Constraint>>(blc));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
答案 6 :(得分:0)
也许您可以使用结构将第二个函数设为私有:
#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
答案 7 :(得分:0)
我首先尝试了PiotrNycz的方法(对于C ++ 03,我被迫用于一个项目),然后我试图找到一种更通用的方法并提出了这个ForcedType<T>
模板类。 / p>
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;
};
如果我没有弄错的话,这三个专业应涵盖所有常见用例。我不确定是否实际需要rvalue-reference(在C ++ 11以上)的专门化或者是按值的专业化。
如果函数有3个参数,第3个参数不允许隐式转换,就会像这样使用它:
function(ParamType1 param1, ParamType2 param2, ForcedType<ParamType3> param3);