WG21 Paper n3030 Rvalue Reference as "Funny" Lvalues を Background だけ訳した(主に私が理解するため)

そのうち江添さんがきっちりとしたエントリーにするだろうけど、折角途中まで訳したし、背景だけ終わらせて置いときます(一部訳せなかったけど)。間違いとか飛躍しすぎとかがあればコメントなりtwitterなりで教えていただけると、私のえーぶんどっかいりょくこーじょーにもなるのでとてもありがたいです。

I. Background
Rvalue references were introduced into C++0x to provide a mechanism for capturing an rvalue temporary (which could previously be done in C++ using traditional lvalue references to const) and allowing modification of its value (which could not). When used in the contexts of reference binding, overload resolution, and template argument deduction, it was desired that a function returning an rvalue reference should behave like a traditional function returning an rvalue. The most straightforward way of achieving that goal was to classify such rvalue reference return values as rvalues, and that approach is embodied in the current draft.

I.背景
rvalue referenceは、rvalue temporaryを捕捉し(これは従来でもconstな値へのlvalue referenceを用いてできた)、それに対して変更できるようにする仕組みとして導入された(これは従来できなかった)。関数のrvalue reference返しは、referenceの束縛・オーバーロード解決・テンプレート引数の推論(template argument deduction)の文脈で用いた際、従来的な関数のrvalue返しのように振る舞うべき、ということが望まれる。この目標を達成するための最も直接的な方法は、このrvalue referenceである戻り値を、rvalueとして分類することであった。そしてこのアプローチは現在のドラフトに取り込まれている。


Unfortunately, however, rvalues have certain characteristics that are incompatible with the intended uses for rvalue references. In particular:

  • Rvalues are anonymous and can be copied at will, with the copy assumed to be equivalent to the original. Rvalue references, however, designate a specific object in memory (even if it is a temporary), and that identity must be maintained.
  • The type of an rvalue is fully known – that is, its type must be complete, and its static type is the same as its dynamic type. By contrast, an rvalue reference must support polymorphic behavior and should be able to have an incomplete type.
  • The type of a non-class rvalue is never cv-qualified. An rvalue reference, however, can be bound to a const or volatile object, and that qualification must be preserved.


だが、rvalueは、rvalue referenceの用途と非互換な特徴を持つ。具体的には、

  • rvalueは名前を持たず、コピーしたオブジェクトはオリジナルと同値であるという仮定で自由にコピーできる。しかしrvalue referenceは、それが一時オブジェクトである場合でも、特定のオブジェクトを指して、ずっと保持し続けなければならない。
  • rvalueの型は完全に知られている。言いかえると、rvalueの型はcompleteでなければならず、rvalueの静的な型は、動的なそれと同じである。対照的に、rvalue referenceはpolymorphicな振る舞いをサポートする必要があり、incomplete typeを持つことを認めるべきである。
  • classでないrvalueの型は決してcv修飾されない。しかしrvalue referenceの場合、constやvolatileなobjectへと束縛することが可能で、その修飾は保存される。


In addition, rvalue references (like traditional lvalue references) can be bound to functions. Treating an rvalue reference return value as an rvalue, however, introduces the novel concept of a function rvalue into the language. There was previously no such idea – a function lvalue used in an rvalue context becomes a pointer-to-function rvalue, not a function rvalue – so the current draft Standard does not describe how such rvalues are to be treated. In particular, function calls and conversions to function pointers are specified in terms of function lvalues, so most plausible uses of rvalue references to functions are undefined in the current wording.

さらにrvalue referenceは、(従来のlvalue referenceのように)関数へと束縛できる。しかし、rvalue referenceである戻り値をrvalueとして扱うのは、関数のrvalueについての、妙な概念を言語に持ちこむ。このようなideaは以前存在しなかった―関数のlvalueはrvalueの文脈で関数へのポインタのrvalueとなる(関数のrvalueではない)―そして、現在のドラフトは、このようなrvalueの扱われ方について述べていない。具体的に、関数呼び出しと関数ポインタへの変換は、関数のlvalueでの話で、つまり関数へのrvalue referenceついてのそれらしき使い方は、現在の文言では定義されていない。

One possible approach to resolving these problems would be to maintain the current approach of treating an rvalue reference return value as an rvalue but to add various caveats to the specification of rvalues so that those coming from rvalue references would have special characteristics. This could be called the “funny rvalue” approach. However, further examination of the current wording of the draft Standard indicates that the problems listed above are probably only the tip of the iceberg: many of the specifications that should apply to the objects to which rvalue references refer, such as object lifetime, aliasing rules, etc., are phrased in terms of lvalues, so the list of rvalue caveats could get quite long.

ここまでで述べた方法では、rvalue referenceである戻り値のrvalue扱いについて現在のアプローチを保ちたいのだが、rvalueの仕様に対して、rvalue referenceに由来するいくつかの特例を付け加えるために、特殊な特徴を持つだろう。これは "funny rvalue" アプローチと呼ぼう。しかし、現在のドラフトをより深く調べると、前述の問題は氷山の一角に過ぎないことが分かった:rvalue referenceが参照するオブジェクトへと適用すべき多くの仕様、例えばオブジェクトのlifetimeやaliasing ruleその他は、lvalueの話として記述されている。つまりrvalueに対する特例の一覧はとても長くなった。

This suggests an alternative approach: that rvalue reference return values should actually be seen as lvalues, with a few exceptions to allow them to be treated as rvalues in the cases where that is intended, i.e., in reference binding, overload resolution, and template argument deduction. This idea, dubbed the “funny lvalue” approach, is the subject of this paper.
(The problems described above are discussed in more detail in the following issues in the Core Language Issues List: 664, 690, 846, and 863.)

この提案は、対照的なアプローチを取る:rvalue referenceである戻り値は、意図したケース(referenceの束縛・オーバーロード解決・テンプレート引数の推論)においてrvalueとして扱われることを認めるためにいくつかの例外を除いて、本当にlvalueと見なすべきだ。この考え方、 "funny lvalue" と呼ばれているアプローチは、このペーパーの主題である。
(上記で述べた問題については、Core Language Issues 664, 690, 846, 863で話題になっている)

前に作ったPP_IS_EMPTYの問題点

以前、PP_TUPLE_SIZEを作るために作ったPP_IS_EMPTYがどうなっていたのか思いだしたら、少しばかり問題がありました。


#define FOO(a, b, c)
PP_IS_EMPTY(e FOO)
eをカンマで区切られていないトークン列として、上記のようなトークン列を引数とすると、置換に失敗してエラーとなってしまいます。詳しく書くと面倒なのでソースを見て分かってください。
これを修正するには、全く別のアプローチが必要になりそうですが、これよりいい方法は思いつきませんでした。
それともう一つ、Wave driverで遊んでいたmelponさんから、可変長引数マクロの使い方が間違っているのではないか、という報告を受けました。
#define FOO(a, ...) という定義の引数付きマクロに対して、 FOO(e) と書くのはどうやら規格的にはwell-formedではないようです。VCやgccは、これを私の意図した通りに展開してしまいますが。
こちらではC++0xのdraftから引用してみます。
n3035 Working Draft, Standard for Programming Language C++ §16.3.1.12

If there is a ... in the identifier-list in the macro definition, then the trailing arguments, including any separating comma preprocessing tokens, are merged to form a single item: the variable arguments. The number of arguments so combined is such that, following merger, the number of arguments is one more than the number of parameters in the macro definition (excluding the ...).

(読み間違えていなければ)前述の形式の引数付きマクロは、マクロ定義に登場するパラメータの数( ... は除く)より1つ以上多くの引数を要求します。で、これが先のコードのどこに影響するのかというと、
#define PP_IS_EMPTY_VIII(a, ...) PP_IS_EMPTY_IX((HELPER_IV_ ## a))
の展開です。PP_IS_EMPTY_VIIIには、PP_IS_EMPTYの引数を展開すると空になる場合、EMPTYというトークンが渡されるのですが、これは1つの引数なので、上記の通り、正しいマクロ置換のコードとは言えません。
これに対する修正は簡単で、PP_IS_EMPTY_Vの定義を、


#define PP_IS_EMPTY_V(a, ...) PP_IS_EMPTY_VI)((a __VA_ARGS__ (), NIL))(
のようにすれば解決します。 PP_IS_EMPTY_VIII に2引数以上渡るようにしただけです。

そもそもなんでタプルをどうのこうのってことになったのかと言うと、ふとプリプロセス時にラムダ式を書けないかと色々考えた挙句、できそうと言う結論に達した(普通に式を書けるわけではない)ので、せっかくだから少しでも見た目がかっこいいタプルで作ってやろうとして、PP_IS_EMPTYを思いだしたわけです。というわけでそのうち作るかもしれないし、作らないです。

Spirit.Phoenix に apply がなかったので作ろうと思ったら bind ができたでござる

名前は apply だけど bind だよ。ちなみに boost::phoenix::bind よりすごい点は、 bind の第一引数を placeholder にできるところ。
あとC++03とC++0xの両方の場合で作ったけど、C++0xおいしいです (^q^)

#include <boost/config.hpp>

#if defined(BOOST_HAS_TR1_UTILITY) && !defined(BOOST_NO_VARIADIC_TEMPLATES) && !defined(BOOST_NO_DECLTYPE) && !defined(BOOST_NO_RVALUE_REFERENCES)
# define APPLY_IMPL_BY_0X_FEATURE 1
#else
# define APPLY_IMPL_BY_0X_FEATURE 0
#endif

#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/spirit/home/phoenix/scope.hpp>
#include <boost/spirit/home/phoenix/function.hpp>
#include <boost/spirit/home/phoenix/statement.hpp>
#include <boost/spirit/home/phoenix/bind.hpp>
#include <boost/ref.hpp>
#include <iostream>
#include <string>

#if APPLY_IMPL_BY_0X_FEATURE
# include <utility>
#else
# include <boost/mpl/assert.hpp>
# include <boost/mpl/begin_end.hpp>
# include <boost/mpl/find.hpp>
# include <boost/mpl/fold.hpp>
# include <boost/mpl/placeholders.hpp>
# include <boost/mpl/transform_view.hpp>
# include <boost/mpl/vector.hpp>
# include <boost/mpl/void.hpp>
# include <boost/function_types/function_type.hpp>
# include <boost/utility/result_of.hpp>

# include <boost/preprocessor/arithmetic/inc.hpp>
# include <boost/preprocessor/comparison/greater.hpp>
# include <boost/preprocessor/facilities/intercept.hpp>
# include <boost/preprocessor/repetition/enum_params.hpp>
# include <boost/preprocessor/repetition/enum_shifted_params.hpp>
# include <boost/preprocessor/repetition/enum_shifted_binary_params.hpp>
# include <boost/preprocessor/repetition/for.hpp>
# include <boost/preprocessor/repetition/repeat.hpp>
# include <boost/preprocessor/repetition/repeat_from_to.hpp>
# include <boost/preprocessor/seq/seq.hpp>
# include <boost/preprocessor/seq/for_each_product.hpp>
#endif

namespace bst = boost;

#if APPLY_IMPL_BY_0X_FEATURE

template<typename T>
T value();

struct apply_t {
template<typename F, typename ...AS>
struct result {
typedef decltype(value<F>()(value<AS>()...)) type;
};
template<typename F, typename ...AS>
typename result<F &&, AS &&...>::type
operator()(F && f, AS && ...as) const {
return std::forward<F>(f)(std::forward<AS>(as)...);
}
};

#else

namespace mpl = bst::mpl;

template<typename Seq, typename T>
struct take : public
mpl::iterator_range<typename mpl::begin<Seq>::type,
typename mpl::find<Seq, T>::type>
{};

#define DEF_APPLY_OVERLOADS_OP(z, n, data) \
BOOST_PP_SEQ_FOR_EACH_PRODUCT(DEF_APPLY_OVERLOADS, \
)((n))( BOOST_PP_REPEAT(n, MAKE_CONST_SEQ, _))

#define DEF_APPLY_OVERLOADS(r, seq) \
DEF_APPLY_OVERLOADS_I(BOOST_PP_SEQ_HEAD(seq), BOOST_PP_SEQ_TAIL(seq))
#define DEF_APPLY_OVERLOADS_I(n, seq) \
template<BOOST_PP_ENUM_PARAMS(n, typename T)> \
typename result<BOOST_PP_ENUM_PARAMS(n, T)>::type \
operator()(BOOST_PP_FOR((n, 0, seq), \
PARAMS_P, \
PARAMS_OP, \
PARAMS_M)) const \
{ \
return param0(BOOST_PP_ENUM_SHIFTED_PARAMS(n, param)); \
}

#define PARAMS_P(r, state) PARAMS_P_I state
#define PARAMS_P_I(n, i, seq) BOOST_PP_GREATER(n, i)

#define PARAMS_OP(r, state) PARAMS_OP_I state
#define PARAMS_OP_I(n, i, seq) (n, BOOST_PP_INC(i), BOOST_PP_SEQ_TAIL(seq))

#define PARAMS_M(r, state) PARAMS_M_I state
#define PARAMS_M_I(n, i, seq) \
BOOST_PP_COMMA_IF(i) T ## i BOOST_PP_SEQ_HEAD(seq) & param ## i

#define MAKE_CONST_SEQ(z, n, _) )(()(const))(

struct apply_t {
template<typename T0,
BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(
PHOENIX_LIMIT,
typename T,
= mpl::void_ BOOST_PP_INTERCEPT)>
struct result {
private:
typedef mpl::vector<BOOST_PP_ENUM_PARAMS(PHOENIX_LIMIT, T)> vec;
public:
typedef typename
bst::result_of<typename
bst::function_types::function_type<typename
take<mpl::transform_view<vec,
bst::unwrap_reference<mpl::_> >,
mpl::void_>::type>::type>::type type;
};
BOOST_PP_REPEAT_FROM_TO(1, PHOENIX_LIMIT, DEF_APPLY_OVERLOADS_OP, _)
};

#endif

namespace p2 {
using namespace bst::phoenix;
using namespace bst::phoenix::arg_names;
using namespace bst::phoenix::local_names;
}

p2::function<apply_t> const apply;

int f(int x, int y) {return x * y;}
int main () {
using std::cout;
using namespace p2;

int n = 42;

cout << apply(_1, n, _2)(bst::cref(&f), bst::cref(2)) << '\n';
// cout << bind(_1, n, _2)(bst::cref(&f)) << '\n'; // これはできない

let (_a = _1) [cout << apply(_a, _2, _3) << '\n'](bst::cref(&f), n, bst::cref(2));

return 0;
}

これはさすがにタイムアウトhttp://ideone.com/YnxoTX5

時代はBoost.Lambdaではなく、Spirit.Phoenixなのだ


#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/spirit/home/phoenix/scope.hpp>
#include <boost/spirit/home/phoenix/bind.hpp>
#include <boost/spirit/home/phoenix/function.hpp>
#include <boost/spirit/home/phoenix/statement.hpp>

#include <string>
#include <iostream>
#include <boost/ref.hpp>
#include <boost/lexical_cast.hpp>

namespace bst = boost;

namespace p2 {
using namespace bst::phoenix;
using namespace bst::phoenix::arg_names;
using namespace bst::phoenix::local_names;
}

struct inc_t {
template<typename T>
struct result {
typedef T type;
};
template<typename T>
T operator()(T x) const {
return x + 1;
}
};

p2::function<inc_t> const inc;

int main () {
using std::cout;
using namespace p2;

int n = 42;

cout << _1(n) << '\n';
(cout << val(42) << '\n')();
(cout << (ref(n) = 24) << '\n')();
(cout << inc(_1) << '\n')(n);
cout << let (_a = _1 + _2,
_b = 5)
[ _a + _b ](n, bst::cref(13))
<< '\n';

let (_a = 0) [
for_ (nothing, _a < _1, ++_a) [
cout << if_else(_a % 15 == 0, "fizz",
if_else(_a % 3 == 0, "buzz",
bind(bst::lexical_cast<std::string, int>, _a)))
<< ' '
],
ref(cout) << '\n'
](bst::cref(15));

return 0;
}
実行結果: http://ideone.com/33mSjQpx
すばらしい実力です。

packageのインストールに関する備忘録

cabal install のリポジトリ

http://hackage.haskell.org/trac/hackage/wiki/CabalInstall
を見ればいいんだけど、おもいっきり間違えた。 cabal install の stable branche は http://darcs.haskell.org/cabal-branches/cabal-install-x.x である。

Setup configure のオプション

make configure と同じく --prefix=$HOME/xxx を付けてローカルに。ついでに --user を指定しておけば、 パッケージの登録がユーザのデータベースに対してなされるようになる。

道のり

  1. 何かcgi書こう
  2. できた!
  3. 日本語出力おかしい
  4. そうだ ghc 6.10 は日本語そのまま入出力できないのだった
  5. utf8-stringがひつようだ
  6. wget して展開してSetupとかめんどい
  7. cabal install だ
  8. Ubuntuのpackageにcabal installないぞ…
  9. よしdarcs getだ
  10. ライブラリが足りないので apt-get
  11. なんかライブラリの要求バージョンおかしい
  12. もしかして:HEADだから
  13. brancheにした
  14. あれ、cabalコマンドがないぞ
  15. cabalとcabal install間違えてたでござる
  16. できた、やっとcabal install使える
  17. sudo apt-get install libghc6-utf8-string-dev あるとか悲しい…というかなんでさっき apt-get したときに気付かなかったの…

tieで初期化させろ take 2

このような指令が来ましたので、やりましょう。


#include <utility>
#include <tuple>
#include <iostream>

#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/eat.hpp>
#include <boost/preprocessor/tuple/rem.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/repetition/for.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/facilities/is_empty.hpp>

template<std::size_t N, typename Tup>
typename std::tuple_element<N, Tup>::type &
get_with_tuple_ref_collapsing(Tup & tup) {
return std::get<N>(tup);
}
template<std::size_t N, typename Tup>
typename std::tuple_element<N, Tup>::type &&
get_with_tuple_ref_collapsing(Tup && tup) {
return std::move(std::get<N>(tup));
}

#define TIE(seq, expr) TIE_I(seq, expr, __LINE__,)
#define TIE_AUTO(seq, expr) TIE_I(seq, expr, __LINE__, auto)
#define TIE_I(seq, expr, id, a) TIE_II(seq, expr, id, a)
#define TIE_II(seq, expr, id, type) \
auto && tie_tmp_ ## id = expr \
BOOST_PP_FOR((tie_tmp_ ## id, type, 0, seq), TIE_P, TIE_OP, TIE_M)

#define TIE_P(_, state) BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM(4, 3, state))
#define TIE_OP(_, state) TIE_OP_I state
#define TIE_OP_I(name, type, n, seq) \
(name, type, BOOST_PP_INC(n), BOOST_PP_SEQ_TAIL(seq))
#define TIE_M(_, state) TIE_M_I state
#define TIE_M_I(name, type, n, seq) \
TIE_M_II(name, type, n, BOOST_PP_SEQ_HEAD(seq))
#define TIE_M_II(name, type, n, e) \
BOOST_PP_IIF(TIE_IGNORE_P(e), BOOST_PP_TUPLE_EAT(1), ; type e) \
(get_with_tuple_ref_collapsing<n>(std::forward<decltype)((name))(>(name)))

#define TIE_IGNORE_P(name) BOOST_PP_IS_EMPTY(name)

int main()
{
TIE)((const int x)(const int y)(const int z), std::make_tuple(1, 2, 3))(;
std::cout << x << "," << y << "," << z << std::endl;

TIE)((const int a)(), std::make_tuple(4, 5))(;
std::cout << a << std::endl;
return 0;
}

TIE_IGNORE_Pを書き換えれば、お好きなキーワードでignore相当の機能を使うことができる親切設計。

バッファ移動とウィンドウ移動

使いこなせていないEmacsに手を入れてみた。と言っても設定レベルだけど、更新しなさすぎなので、書く。

; となりにあることが分かっているのにC-bとか面倒すぎるので、C-tabでウィンドウのバッファを適当な順番で送る
(global-set-key [C-tab] 'bury-buffer)
(global-set-key [C-S-iso-lefttab]
                '(lambda ()
                   (interactive)
                   (switch-to-buffer (nth (1- (length (buffer-list)))
                                          (buffer-list)))))
; C-←とかでWindow切り替え
(setq windmove-wrap-around t)
(global-set-key [C-right] 'windmove-right)
(global-set-key [C-left] 'windmove-left)
(global-set-key [C-up] 'windmove-up)
(global-set-key [C-down] 'windmove-down)

追記:ウィンドウを移動するコマンドについてすっかり忘れていたので、追加