How do I avoid implicit conversions on non-constructing functions?

后端 未结 7 1857
孤城傲影
孤城傲影 2020-11-27 17:25

How do I avoid implicit casting on non-constructing functions?
I have a function that takes an integer as a parameter,
but that function will also take characters, b

相关标签:
7条回答
  • 2020-11-27 17:49

    Well, I was going to answer this with the code below, but even though it works with Visual C++, in the sense of producing the desired compilation error, MinGW g++ 4.7.1 accepts it, and invokes the rvalue reference constructor!

    I think it must be a compiler bug, but I could be wrong, so – anyone?

    Anyway, here's the code, which may turn out to be a standard-compliant solution (or, it may turn out that that's a thinko on my part!):

    #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
    }
    
    0 讨论(0)
  • 2020-11-27 17:54

    For C++14 (and I believe C++11), you can disable copy constructors by overloading rvalue-references as well:

    Example: Say you have a base Binding<C> class, where C is either the base Constraint class, or an inherited class. Say you are storing Binding<C> by value in a vector, and you pass a reference to the binding and you wish to ensure that you do not cause an implicit copy.

    You may do so by deleting func(Binding<C>&& x) (per PiotrNycz's example) for rvalue-reference specific cases.

    Snippet:

    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));
    }
    

    Output:

    >>> 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>&
    

    Error (with clang-3.9 in bazel, when offending line is uncommented):

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

    Full Source Code: prevent_implicit_conversion.cc

    0 讨论(0)
  • 2020-11-27 18:06

    Here's a general solution that causes an error at compile time if function is called with anything but an int

    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;
    }
    

    It works by allowing any type for the argument to function but using is_int as a type-level predicate. The generic implementation of is_int has a false value but the explicit specialization for the int type has value true so that the static assert guarantees that the argument has exactly type int otherwise there is a compile error.

    0 讨论(0)
  • 2020-11-27 18:07

    You can't directly, because a char automatically gets promoted to int.

    You can resort to a trick though: create a function that takes a char as parameter and don't implement it. It will compile, but you'll get a linker error:

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

    Calling the function with a char parameter will break the build.

    See http://ideone.com/2SRdM

    Terminology: non-construcing functions? Do you mean a function that is not a constructor?

    0 讨论(0)
  • 2020-11-27 18:07

    Maybe you can use a struct to make the second function private:

    #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;}
    

    This won't compile:

    prog.cpp: In function ‘int main()’:
    prog.cpp:7: error: ‘static void NoCast::function(char)’ is private
    prog.cpp:16: error: within this context
    
    0 讨论(0)
  • 2020-11-27 18:08

    I first tried PiotrNycz's approach (for C++03, which I'm forced to use for a project), then I tried to find a more general approach and came up with this ForcedType<T> template class.

    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;
    };
    

    If I'm not mistaken, those three specializations should cover all common use cases. I'm not sure if a specialization for rvalue-reference (on C++11 onwards) is actually needed or the by-value one suffices.

    One would use it like this, in case of a function with 3 parameters whose 3rd parameter doesn't allow implicit conversions:

    function(ParamType1 param1, ParamType2 param2, ForcedType<ParamType3> param3);
    
    0 讨论(0)
提交回复
热议问题