3

How to partial specialization that two template parameter are same type.

How to make this code using second function .

#include <utility>
#include <iostream>

template <typename A, typename B>
void Translate(A&& a,B* b){
  // make some translate from a to b
  // b->bvalue=a.av;
  std::cout<<"normal function";
}
//if a and b are same type,
template <typename A>
void Translate(A&& a, A* b) {
  *b = std::forward<A>(a);
  std::cout<<"forward function";
}

int main(int argc, char** argv) {
  int in=0,out=0;
  Translate(in,&out);
  return 0;
}

Expect out put "forward function".

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Zhigang Zhang
  • 195
  • 1
  • 7
  • In the second overload `A` is being deduced as `int&` from the first parameter, and `int` from the second parameter, which makes it ambiguous and not viable. – Passer By Jun 14 '19 at 10:07
  • 2
    Note that this is not partial specialization (you cannot partially specialize a function template), it's simply two different primary function template overloads of the `Translate` function. As PasserBy mentions, the first overload identified as a better match in overload resolution. – dfrib Jun 14 '19 at 10:09

2 Answers2

5

The problem is that the 2nd version can't be called at all. You're declaring the 1st parameter as fowarding reference, when being passed an lvlaue A will be deduced as T&. Then for the 1st parameter A will be deduced as int&, for the 2nd parameter A will be deduced as int, they conflict.

You can use std::remove_reference for type adjustment. And to solve the following ambiguity issue you can use SFINAE, to exclude undesired specializations from the overload set.

// if A and B are different types
template <typename A, typename B>
std::enable_if_t<!std::is_same_v<std::remove_reference_t<A>, B>> Translate(A&& a,B* b){
  ...
}

// if they're the same type (as A) 
template <typename A>
void Translate(A&& a, std::remove_reference_t<A>* b) {
  ...
}

LIVE

BTW: Function templates can't be partial specialized; as your code showed they can only be overloaded.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Thanks. `std::enable_if_t<!std::is_same, B>::value > Translate(A&& a,B* b)` get right output. – Zhigang Zhang Jun 14 '19 at 10:22
  • 1
    Possibly worth explicitly pointing out, for readers not too familiar with `std::enable_if`, that we are relying here on the fact that its conditionally existing `::type` typedef defaults to `void`, and as these two overloads just so happens to be be `void` functions we can use `std::enable_if` to enable/disable an overload via its return type, instead of the most commonly seen approach of doing it via an additional template parameter. – dfrib Jun 14 '19 at 10:30
  • If I except `template <> void Translate(int&& a,double* b){ std::cout<<"double function"< – Zhigang Zhang Jun 14 '19 at 12:06
  • @ZhigangZhang `int&& a` makes `a` an rvalue-reference, and `in` is an lvalue which can't be bound to `a`. You need to call it like `Translate(std::move(in),&double_out);`. – songyuanyao Jun 14 '19 at 15:44
1

You need to pass in an rvalue for the first parameter. Both of the following print "forward function".

Translate(0, &out);
// or
Translate(std::move(in), &out);

With Translate(in, out), the second overload can't deduce the A type consistently: the first parameter is deduced as int& while second parameter is deduced as int.

main.cpp:12:6: note: candidate: 'template<class A> void Translate(A&&, A*)'
 void Translate(A&& a, A* b) {
      ^~~~~~~~~
main.cpp:12:6: note:   template argument deduction/substitution failed:
main.cpp:19:21: note:   deduced conflicting types for parameter 'A' ('int&' and 'int')
   Translate(in, &out);

So the compiler resorts to the first overload.

TrebledJ
  • 8,713
  • 7
  • 26
  • 48