问题
I know you can use C++ keyword 'explicit' for constructors of classes to prevent an automatic conversion of type. Can you use this same command to prevent the conversion of parameters for a class method?
I have two class members, one which takes a bool as a param, the other an unsigned int. When I called the function with an int, the compiler converted the param to a bool and called the wrong method. I know eventually I'll replace the bool, but for now don't want to break the other routines as this new routine is developed.
回答1:
No, you can't use explicit, but you can use a templated function to catch the incorrect parameter types.
With C++11, you can declare the templated function as delete
d. Here is a simple example:
#include <iostream>
struct Thing {
void Foo(int value) {
std::cout << "Foo: value" << std::endl;
}
template <typename T>
void Foo(T value) = delete;
};
This gives the following error message if you try to call Thing::Foo
with a size_t
parameter:
error: use of deleted function
‘void Thing::Foo(T) [with T = long unsigned int]’
In pre-C++11 code, it can be accomplished using an undefined private function instead.
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);
};
The disadvantage is that the code and the error message are less clear in this case, so the C++11 option should be selected whenever available.
Repeat this pattern for every method that takes the bool
or unsigned int
. Do not provide an implementation for the templatized version of the method.
This will force the user to always explicitly call the bool or unsigned int version.
Any attempt to call Method
with a type other than bool
or unsigned int
will fail to compile because the member is private, subject to the standard exceptions to visibility rules, of course (friend, internal calls, etc.). If something that does have access calls the private method, you will get a linker error.
回答2:
No. explicit
prevents automatic conversion between specific classes, irrespective of context. And of course you can't do it for built-in classes.
回答3:
The following is a very basic wrapper that can be used to create a strong 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
}
One nice feature of this approach, is that you can also distinguish between different parameters with the same type. For example you could have the following:
class WidthTag;
typedef StrongType<int, WidthTag> Width;
class HeightTag;
typedef StrongType<int, HeightTag> Height;
void foo (Width width, Height height);
It will be clear to the clients of 'foo' which argument is which.
回答4:
Something that might work for you is to use templates. The following shows the template function foo<>()
being specialized for bool
, unsigned int
, and int
. The main()
function shows how the calls get resolved. Note that the calls that use a constant int
that don't specify a type suffix will resolve to foo<int>()
, so you'll get an error calling foo( 1)
if you don't specialize on int
. If this is the case, callers using a literal integer constant will have to use the "U"
suffix to get the call to resolve (this might be the behavior you want).
Otherwise you'll have to specialize on int
and use the "U"
suffix or cast it to an unsigned int
before passing it on to the unsigned int
version (or maybe do an assert that the value isn't negative, if that's what you want).
#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);
}
回答5:
The currently accepted answer (using a private templated function) is nice, but outdated. With C++11, we can use delete
d functions instead:
#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;
}
This conveys the intent of the source code more directly and supplies the user with a clearer error message when trying to use the function with disallowed parameter types.
回答6:
Compiler gave "ambiguous call" warning, which will be sufficient.
I was doing TDD development and didn't realize I forgot to implement the corresponding call in the mock object.
回答7:
bool IS an int that is limited to either 0 or 1. That is the whole concept of return 0;, it is logically the same as saying return false;(don't use this in code though).
回答8:
You could also write an int version that calls the bool one.
来源:https://stackoverflow.com/questions/175689/can-you-use-keyword-explicit-to-prevent-automatic-conversion-of-method-parameter