12

gcc4.7およびboost1.49を使用すると、次の式を使用してis_assignable戻り値が返されます。true

typedef boost::function<void()> F;
std::is_assignable<F, std::nullptr_t>::value

ただし、このコードはコンパイルに失敗します。

boost::function<void()> f;
f = nullptr;

これらのエラーメッセージを生成します。

In file included from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/detail/maybe_include.hpp:13:0,
             from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/detail/function_iterate.hpp:14,
             from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/preprocessor/iteration/detail/iter/forward1.hpp:47,
             from c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function.hpp:64,
             from ..\main.cpp:8:
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp: In instantiation of 'static void boost::detail::function::void_function_obj_invoker0<FunctionObj, R>::invoke(boost::detail::function::function_buffer&) [with FunctionObj = std::nullptr_t; R = void]':
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:907:60:   required from 'void boost::function0<R>::assign_to(Functor) [with Functor = std::nullptr_t; R = void]'
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:722:7:   required from 'boost::function0<R>::function0(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = std::nullptr_t; R = void; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]'
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:1042:16:   required from 'boost::function<R()>::function(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = std::nullptr_t; R = void; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]'
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:1083:5:   required from 'typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R()>&>::type boost::function<R()>::operator=(Functor) [with Functor = std::nullptr_t; R = void; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R()>&>::type = boost::function<void()>&]'
..\main.cpp:172:6:   required from here
c:\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/boost/function/function_template.hpp:153:11: error: '* f' cannot be used as a function

さらに、この式は次を返しますfalse

typedef boost::function<void()> G;
std::is_assignable<G, decltype(NULL)>::value

しかし、このコードはコンパイルされます:

boost::function<void()> g;
g = NULL;

の結果はis_assignable、の機能を適切に反映していないようですboost::function。私はここで何か間違ったことをしていますか?(エラーメッセージを理解するのに問題があります。)

タイプ特性は、テンプレートで使用されるクラスの機能を決定するための信頼できる方法であると思われました。C ++ 11で提供されるタイプ特性は、単にboost :: functionと互換性がありませんか?


これにいくつかのコンテキストを与えるために、私はC++11の新機能をよりよく理解するためにいくつかの個人的なプロジェクトに取り組んできました。この特定のプロジェクトでは、「非アクティブ化」できる呼び出し可能な関数を格納するクラスを作成しようとしています。これは大まかに私がやろうとしていることです:

template <typename F>
class callable_function
{
public:
    callable_function(F func) : func_(func)
    {
        /* func_ is initially active */
    }

    void call()
    {
        if (/* func_ is active */) func_();
    }

    void deactivate()
    {
        /* set func_ to deactive */
    }

private:
    F func_;
};

/* func_ is active */およびブロックについては/* set func_ to deactive */、のプロパティに応じてコンパイル時に選択される2つの異なる実装を提供したいと思いますFnullptrブールコンテキストに割り当てることができ、ブールコンテキストfunc_で使用できる場合func_は、次のものを使用します(これは、組み込み関数ポインター用に選択されるものですstd::function)。

template <typename F>
class callable_function
{
public:
    callable_function(F func) : func_(func) {}

    void call()
    {
        if (func_) func_();
    }

    void deactivate()
    {
        func_ = nullptr;
    }

private:
    F func_;
};

nullptrに割り当てることができない場合はfunc_、「アクティブ」ステータスを格納するクラス内に追加のブール値を格納します。この実装は、ファンクターとラムダ関数用に選択されています。

template <typename F>
class callable_function
{
public:
    callable_function(F func) : func_(func), active_(true) {}

    void call()
    {
        if (active_) func_();
    }

    void deactivate()
    {
        active_ = false;
    }

private:
    F func_;
    bool active_;
};

nullptr現在はに割り当てることができないため、boost::function2番目の実装が選択されることを期待します。ただし、はとis_assignableを返すtrueためboost::functionnullptr代わりに最初の実装が選択され、関数でコンパイルエラーが発生しdeactivateます。

4

1 に答える 1

8

[自分の質問に答えるのは気が引けるが、それについて多くのことを学んだので、その情報をここにまとめるのが最善だと思った。ジェシーは私がこれらすべてを理解するのを助けてくれた大きな部分だったので、上記の彼のコメントに賛成してください。]

では、なぜis_assignable次の結果が返されるのでしょうか。

typedef boost::function<void()> F;
std::is_assignable<F, std::nullptr_t>::value // true
std::is_assignable<F, decltype(NULL)>::value // false

これらのステートメントがそれらの結果と矛盾しているように見えるという事実にもかかわらず:

boost::function<void()> f;
f = nullptr; // fails to compile
f = NULL;    // compiles correctly

最初に注意することは、標準ライブラリの操作ベースの型特性(、、、is_constructibleなど)はis_assignableis_convertibleテンプレートに指定された型と一致する有効なインターフェイスを持つ関数のみをチェックすることです。特に、これらの型が関数本体に代入されたときに、その関数の実装が有効かどうかを確認しません。

boost::functionの特定のコンストラクターはありませんがnullptr、「キャッチオール」テンプレート代入演算子(対応するコンストラクターとともに)があります。

template<typename Functor>
BOOST_FUNCTION_FUNCTION& operator=(Functor const & f);

nullptrに特定のオーバーロードがなく、std::nullptr_t別のタイプへの変換(への変換を除く)を必要としないため、これはに最適const &です。テンプレート置換でこの代入演算子が見つかったため、をstd::is_assignable<boost::function<void()>, std::nullptr_t>返しますtrue

ただし、この関数の本体内ではFunctor、呼び出し可能な型であることが期待されます。つまりf();、有効なステートメントであることが期待されます。 nullptrは呼び出し可能なオブジェクトではないため、次のコードを実行すると、質問にリストされているコンパイラエラーが発生します。

boost::function<void()> f;
f = nullptr; // fails to compile

しかし、なぜstd::is_assignable<boost::function<void()>, decltype(NULL)>戻るのfalseですか? boost::functionパラメータに特定の代入演算子がないのに、とに使用されるのとint同じ「キャッチオール」テンプレート代入演算子がないのはなぜですか?intstd::nullptr_t

以前、メタプログラミングの側面を省略してこの代入演算子のコードを簡略化しましたが、現在は関連しているので、それらを追加し直します。

template<typename Functor>
typename enable_if_c<
           (boost::type_traits::ice_not<
             (is_integral<Functor>::value)>::value),
           BOOST_FUNCTION_FUNCTION&>::type
operator=(Functor const & f)

パラメータのタイプがである場合(つまり、returnsの場合)enable_if_cにこの代入演算子のインスタンス化を防ぐために、ここでメタプログラミング構造が使用されていることはかなり自明です。したがって、代入ステートメントの右側がタイプの場合、に一致する代入演算子はありません。これが、がタイプであるためにを返す理由です(少なくともGCCの場合)。intis_integraltrueintboost::functionstd::is_assignable<boost::function<void()>, decltype(NULL)>falseNULLint

f = NULL;しかし、これはまだ正しくコンパイルされる理由を説明していません。0これを説明するために、値は暗黙的に任意のポインタ型に変換可能 であることに注意することが重要です。boost::functionプライベート構造へのポインタを受け入れる代入演算子を使用して、これを利用します。(以下は、からのコードの大幅に簡略化されたバージョンですが、boost::function私のポイントを示すには十分です):

namespace boost
{
    template<typename R()>
    function
    {
    private:
        struct clear_type {}
        //...

    public:
        BOOST_FUNCTION_FUNCTION& operator=(clear_type*);
        //...
    };
}

はプライベート構造であるためclear_type、外部コードはそのインスタンスを作成できません。この代入演算子が受け入れることができる唯一の値は、から暗黙的に変換されたnullポインターです0。これは、式で呼び出される代入演算子ですf = NULL;


これで、と割り当てステートメントがそのように機能する理由が説明されますis_assignableが、それでも元の問題を解決するのに役立ちません。特定のタイプが受け入れることができるかどうかを検出するにはどうすればnullptrよいNULLですか。

残念ながら、有効なインターフェイスが存在するかどうかを検出するだけの機能があるため、タイプ特性にはまだ制限があります。についてnullptrは、良い答えはないようです。を使用boost::functionすると、に対して有効なインターフェイスが存在しますがnullptr、このタイプの関数本体の実装は無効であるため、などのステートメントに対して常にコンパイラエラーが発生しますf = nullptr;

しかし、コンパイル時に NULL、などの特定のタイプに割り当てることができるものを正しく検出できますか?2番目の引数の型を指定する必要があります。これはに評価されるので、それが機能しないことはすでにわかっています。タイプとして使用することもできますが、これは非常に言葉が多く、使用しているタイプの内部の詳細を知っている必要があります。boost::functionstd::is_assignabledecltype(NULL)intboost::function<void()>::function::clear_type*

エレガントなソリューションには、カスタムタイプの特性を作成することが含まれます。これは、SOに関する別の投稿のLucDantonからのものです。このアプローチの詳細については、他の質問で詳しく説明されているため説明しませんが、カスタムタイプの特性のコードは次の場所にあります。

template<typename> struct Void { typedef void type; };

template<typename T, typename Sfinae = void>
struct is_assignable_with_NULL: std::false_type {};

template<typename T>
struct is_assignable_with_NULL<T, 
    typename Void< decltype( std::declval<T>() = NULL ) >::type
>: std::true_type {};

この新しいタイプの特性は、と同様に使用できますstd::is_assignableが、左側にオブジェクトのタイプを指定するだけで済みます。

is_assignable_by_NULL<boost::function<void()>::value

boost::functionすべての型特性と同様に、これでも有効なインターフェイスのみをチェックし、関数本体の有効性を無視しますが、最終的に、コンパイル時にNULL(およびその他の型)を割り当てることができるかどうかを正しく判断できます。

于 2012-04-23T20:41:27.190 に答える