Template specialisation with default argument [duplicate]

血红的双手。 提交于 2019-12-22 09:29:43

问题


I have a program that is as follows. There is a base template struct X and a partial specialisation with SFINAE.

template <typename T, typename U = void>
struct X{
  X() {
    std::cout << "in 1" << std::endl;
  };
};

template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>> > {
  X() {
    std::cout << "in 2" << std::endl;
  };
};

int main() {
  X<int> x;
}

When running the program in 2 is printed.

  1. Why is it that the second specialization is chosen over the first since both of them effectively declare a struct X<int, void>. What makes std::enable_if_t<std::is_integral_v<T>> more specialized than a default template type argument as shown in the base template?

  2. Why does the default type argument of the base template have to be the same as the type defined by the partial specialization for the partial specialization to be called and in 2 to be printed. Why does changing to std::enable_if_t<std::is_integral_v<T>, bool> cause the base template in 1 to be called?


回答1:


The answers to your questions lie in Template Partial Ordering. This is the mechanism the compiler uses to determine which template is the best fit (be it a function template overload, or in your case, a class template specialization).

In brief, your generic template implementation has 2 parameters T and U, whereas your SFINAE specialization have only the T parameter, while the second is deduced from T. It is therefore more specialized than the general case and in the end, when you refer to X<int, void>, the specialization is chosen.

Now question 2. Suppose we replace the enable_if parameter with bool instead of void. Now our specialization will be X<int, bool> instead of X<int, void>, so when you refer to X<int>, i.e. X<int, void>, it doesn't match the specialization anymore because those are 2 different types.




回答2:


1) [...] What makes std::enable_if_t> more specialised than a default template type argument as shown in the base template?

So do you know that, if two template match, the more specialized is selected.

Well... the second one is more specialized because if X matches the specialization (so if X is an integral type), it's that matches also the generic version.

But exist couples of types (by example: std::string, void) that matches the generic version and doesn't matches the specialization.

So the specialization is more specialized that the generic version, so is preferred when both template match.

Why does the default type argument of the base template have to be the same as the type defined by the partial specialisation for the partial specialisation to be called and in 2 to be printed. Why does changing to std::enable_if_t, bool> cause the base template in 1 to be called?

You have to understand how works the trick of the default type value.

You have that the generic version that is

template <typename T, typename U = void>
struct X;

so writing X<int> x, you're writing X<int, void> x; and surely matches the generic version.

The specialization is

template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>>>;

Question: X<int, void> matches X< T, std::enable_if_t<std::is_integral_v<T>>> ?

Answer: yes, because int is integral, so std::enable_if_t<std::is_integral_v<T>> is substituted with void.

Suppose now that the generic specialization become

template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>, bool>>

We have that X<int> x is again X<int, void> x and matches again the generic version.

Question: X<int, void> matches also X< T, std::enable_if_t<std::is_integral_v<T>, bool>> ?

Answer: no, because std::enable_if_t<std::is_integral_v<T>, bool> become bool and X<int, void> doesn't matches X<int, bool>

So the generic version is the only one that matches and is selected.



来源:https://stackoverflow.com/questions/51479015/template-specialisation-with-default-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!