ユーザ定義リテラルまとめ


このエントリは見事に間違ってたので忘れてください。><
グローバルな名前空間でもユーザ定義リテラルは定義できます。

参考 http://cpplover.blogspot.com/2009/09/user-defined-literal.html

ところで新たな疑問があったのでここにメモ。

template <char... C> std::string operator "" _c();

int main()
{
  L"ABCDE"_c;  // これはtemplate <char... C> std::string operator "" _c()でどう展開される?
}

わからない・・・。
ユーザ定義リテラルを使用したい場合はどうやら名前空間を作ってそこに書かなければいけないようです。
つまりこう。

namespace A {
  constexpr long double operator "" _km(long double distance)
  {
    return distance * 1000;
  }
}

int main()
{
  {
    using namespace A;  // or using A::operator "" _km;
    long double meter = 1.2_km;
  }

  return 0;
}

名前空間Aの_kmを使用する事を保障するためには、上記のようにスコープを切ってusing-directiveかusing-declarationで宣言しておく必要があります。
これはC++を規格を読んで書こうと言う人であれば気づくので問題ではありません。
しかし、一般のbetter CとしてしかC++を使っていない人はコンパイルが通って動きさえすればそれで良いのです。
その場合以下のようなコードが世の中に広まってしまうのではないかと不安です。

constexpr long double operator "" _km(long double distance)
{
  return distance * 1000;
}

int main()
{
  long double meter = 1.2_km;

  return 0;
}

これはおそらくほとんどの環境で動作するでしょう。
しかしこれはC++標準の規格違反であり処理系や、ライブラリで_kmが定義されusing-directiveで
ライブラリの名前空間を宣言したとたんにコンパイルが通らなくなるでしょう。
例えば以下は曖昧さが残るためコンパイル不可

namespace A {
  constexpr long double operator "" _km(long double distance)
  {
    return distance * 1000;
  }
}

constexpr long double operator "" _km(long double distance)
{
  return distance * 1000;
}

int main()
{
  using namespace A;
  long double meter = 1.2_km;  // _kmは曖昧でありエラー_kmは処理系で定義されるかもしれない

  return 0;
}

曖昧さが残らないように書くためには以下のようにグローバル名前空間にユーザ定義リテラルは定義せずに必ず名前空間を作りその中に定義すること。

namespace A {
  constexpr long double operator "" _km(long double distance)
  {
    return distance * 1000;
  }
}

namespace B {
  constexpr long double operator "" _km(long double distance)
  {
    return distance * 1000;
  }
}

int main()
{
  {
    using A::operator "" _km;
    long double meter = 1.2_km;
  }
  {
    using B::operator "" _km;
    long double meter = 1.2_km;
  }

  return 0;
}

これで曖昧さが無くなりコンパイルエラーを引き起こすことも無く使用することが出来ます。
using namespace A;ではなく、using A::operator "" _km;で書いた理由は処理系でグローバル名前空間に_kmが定義された場合に
曖昧さを引き起こすため、これを回避するためです。



参考
ユーザ定義リテラルはclassのfriendとして宣言することもできます。
constexprをつけない場合、ユーザ定義リテラルコンパイル時に解析されるかどうかは処理系依存です。


追記:typoの指摘をしていただいたのでコードのusing宣言部を修正しました。