LoginSignup
5
1

関数テンプレート経由で振り分ける

Last updated at Posted at 2019-05-11

C++ において std::pow のオーバーロードの解決が曖昧になる場合を取り上げている記事を読みました。

C++ Builder 10.2 Tokyo > E2015 std::pow(long double, int)std::pow(double, int) の区別が曖昧 > 対処

これを機にふと std::pow について眺めてみると、 C++03 では確かにこれは曖昧であるようですし、 C++11 でも曖昧です。

それはこのようなコードで試してみるとわかります。

sample1.cpp
#include <iostream>

double foo(double) {
  std::cout << "double" << std::endl;
  return 0;
}

long double foo(long double) {
  std::cout << "long double" << std::endl;
  return 0;
}

int main(void) {
  foo(int());
  return 0;
}

ですが、 C++11 では std::pow(int(), int()) といったように実引数の型が int である場合も特にキャストする必要はなく、最終的に double pow(double, double) を呼出します。 整数を与えた場合は std::pow と同名の関数テンプレートが一旦受け取って振り分けなおす仕組みだからです。

上の例を少し弄って表現するとこのような要領です。

sample2.cpp
#include <iostream>

double foo(double) {
  std::cout << "double" << std::endl;
  return 0;
}

long double foo(long double) {
  std::cout << "long double" << std::endl;
  return 0;
}

#include <type_traits>

template<class T>
typename std::enable_if<std::is_integral<T>::value, double>::type
foo(const T& x) {
  return foo(static_cast<double>(x));
}

int main(void) {
  foo(int());
  return 0;
}

オーバーロードの解決規則は大変に複雑なのですが、大原則としては型変換なしで型が完全に一致するものがあればそれが最優先です。 完全一致がなければ型変換による一致を試す前にテンプレートが候補になります。 候補になった関数テンプレートの中で上手く振り分ければ、本来は曖昧なはずのオーバーロードも上手く解決できる場合があります。

ごく簡単な関数であれば振り分けなおしなどといった処置をするまでもなくひとつの関数テンプレート内で if constexpr で分岐してもよいと思うのですが、既存のオーバーロードに曖昧さがあることに気づいたときなどは、関数テンプレートを経由するというのは有用な手法足り得るので感心した次第です。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1