Overloaded non-type template is ambiguous while non-templated function is ok

后端 未结 3 545
庸人自扰
庸人自扰 2021-01-11 12:29

If we have a template function which takes a non-type parameter of type int or short the compiler complains about the ambiguity of the following ca

相关标签:
3条回答
  • 2021-01-11 13:10

    The compiler error message is missleading*. You would intuitively think that it means that "The function invocation is ambigious!", but in reality the compilation fails in an earlier stage, the definition of the specialized function is not even generated at that point.

    What it really means is this: "The function specialization is ambigious!"

    Lets see how this compiles:

    template <short S> void foo() { std::cout << "S: " << S << '\n'; }
    
    int main(int argc, char* argv[])
    {
        foo<0>();
        return 0;
    }
    

    The first step of the compilation is the template specialization.

    Step 1: The compiler realizes that foo<0> is a template specialization, and generates a function declaration accordingly:

    template <short S> void foo() { std::cout << "S: " << S << '\n'; }
    
    template<>
    void foo<0>();
    
    int main(int argc, char* argv[])
    {
        foo<0>();
        return 0;
    }
    

    Step 2: The compiler relizes that the function is actually invoked (This seems obvious in this case, but it is less obvious when you have a class template.), and generates a definition:

    template <short S> void foo() { std::cout << "S: " << S << '\n'; }
    
    template<>
    void foo<0>();
    
    int main(int argc, char* argv[])
    {
        foo<0>();
        return 0;
    }
    
    template<>
    void foo<0>(){
        std::cout << "S: " << 0 << '\n';
    }
    

    Step 3: Now you have a callable function, the compilation continues normally.

    Lets try to follow the same steps in your case:

    template <short S> void foo() { std::cout << "S: " << S << '\n'; }
    template <int I>   void foo() { std::cout << "I: " << I << '\n'; }
    
    int main(int argc, char* argv[])
    {
        foo<0>();
        return 0;
    }
    

    Step 1: Generating the function declaration:

    template <short S> void foo() { std::cout << "S: " << S << '\n'; }
    template <int I>   void foo() { std::cout << "I: " << I << '\n'; }
    
    template<>
    void foo<0>();
    
    int main(int argc, char* argv[])
    {
        foo<0>();
        return 0;
    }
    

    And at this point the compilation fails, because the specialized declaration of foo is ambigious. If you want proof, try compiling this code:

    template <short S> void foo() { std::cout << "S: " << S << '\n'; }
    template <int I>   void foo() { std::cout << "I: " << I << '\n'; }
    
    template<>
    void foo<0>();
    
    int main(int argc, char* argv[])
    {
        return 0;
    }
    

    You will get the same error message without the function invocation!

    Update

    So the takeaway is that everything translates to specialized function declarations. So no matter if you write foo<int{0}> of foo<short{0}>, the compiler will generate template<> void foo<0>(); for both. The explicit types will be ignored. (Thats why its really important that they are constexpr-s.)

    Update As T.C. pointed out in his comment, in the standard (413rd page in the PDF) there is a very similar example:

    [Example: In the following example, assuming a signed char cannot represent the value 1000, a narrowing conversion (8.5.4) would be required to convert the template-argument of type int to signed char, therefore substitution fails for the second template (14.3.2).

     template <int> int f(int);
     template <signed char> int f(int);
     int i1 = f<1000>(0); // OK
     int i2 = f<1>(0); // ambiguous; not narrowing
    

    —end example]


    *The error message is completely correct. Might not be particularly intuitive, but it reflects the procedure specified in the standard. – T.C.

    0 讨论(0)
  • 2021-01-11 13:18

    Template function here is parametrized by value, and only by value, not by type! update: now I am not sure. At the other hand, not templated version is parametrized by type, (and one can enjoy polymorphic calls).

    update: Well, looks like instantiated functions mangled name actually depends on type of numerical template parameter.

    0 讨论(0)
  • 2021-01-11 13:35

    Here's what happens when you write f<0>().

    1. The compiler looks up f, and finds two function template declarations:

      template <int I>   void foo();
      template <short S> void foo();
      
    2. The compiler sees the explicit template argument list and attempts to substitute it into each function template declaration:

      template <int I>   void foo(); // with I = 0
      template <short S> void foo(); // with S = 0
      

      Substitution succeeds in both cases because 0 is an int, and can be converted to short, and the conversion is an allowed conversion in this context.

    3. After substitution, two candidate function specializations are produced. Both are viable. Overload resolution is then performed - and since the signature is identical and no tiebreaker applies, overload resolution fails and the call is ambiguous.

    The point here is that the normal overload resolution rules do not apply to template arguments. The conversions for template arguments are applied in an earlier stage, before the regular overload resolution takes place.

    0 讨论(0)
提交回复
热议问题