decltypeを使ってのプログラミングについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

decltypeを使ってのプログラミングについて

#1

投稿記事 by GRAM » 14年前

いつもお世話になっております。GRAMです。
VisualStudioではC++0xの拡張機能がいくつかあり、それら及び
テンプレートを使って、ベクトルのクラスをカスタマイズしているのですが
半日以上デバッグしても以下のような、ほんの数行のプログラムのコンパイルを通すことができません。
やりたいこと:
ベクトルの内積、および定数倍の実装を行う。
しかし戻り値の型は 二つの引数の型によって決まるようにしたい。
動機は以下のようなものです
► スポイラーを表示
operator*の戻り値がわかればよいわけですからdecltypeを使い以下のようなプログラムを書きました。
(必要のない部分は大幅に端折ってあります)

コード:

#include<type_traits>
 
//空のクラステンプレートパラメータ用
struct Empty{};
 
namespace Linear{
 
	
    namespace Private{
		//掛け算の戻り値の型からconstを外す
        template <typename LHS, typename RHS >
        struct RCMul{
            typedef typename std::remove_const< decltype( LHS(0) * RHS(0) ) >::type type;
            //typedef  Empty type;
        };
 
		//掛け算の戻り値の型を得る
        template <typename LHS, typename RHS >
        struct CMul{
            typedef decltype( LHS(0) * RHS(0) ) type;
        };
    }
 
    template<typename NType>
    struct Vector{
    public:
        NType x;
        NType y;
        NType z;
 
        Vector():x(0),y(0),z(0){}
 
        template <typename OtherType>
        Vector( OtherType init_x, OtherType init_y, OtherType init_z )
            :x(init_x), y(init_y), z(init_z){
        }
 
        template <typename OtherType>
        Vector( const Vector<OtherType>& other )
            :x(other.x), y(other.y), z(other.z){
        }
 
        template <typename OtherType>
        Vector& operator=( const Vector<OtherType>& other ){
            x=other.x;
            y=other.y;
            z=other.z;
            return *this;
        }
 
        
    };
 
    //内積
    template < typename NType1, typename NType2 >
    typename Private::CMul< NType1, NType2 >::type
    operator* ( const Vector<NType1>& lhs, const Vector<NType2>& rhs ){

        return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
    
	}
 
    //スカラーとの掛け算   
    template <typename NType1, typename NType2>
    const Vector< typename Private::RCMul< NType1, NType2 >::type >
    operator*( const Vector<NType1>& v,  NType2 s ){

        return Vector< typename Private::RCMul< NType1, NType2 >::type >(v.x*s, v.y*s, v.z*s);

    }
 
 
    template < typename NType1, typename NType2 >
    const Vector< typename Private::RCMul< NType1, NType2 >::type >
    operator*( NType1 s, const Vector<NType2>& v ){

        return Vector< typename Private::RCMul< NType1, NType2 >::type >(s*v.x, s*v.y, s*v.z);
    
	}
}
 
using namespace Linear;
 
 
int main(){
    Vector<double> v(1.0, 1.0, 0.0 );
    Vector<double> u = v;
    double d = v*u;
} 
このコードのコンパイルが通りません。
なぜでしょうか?

環境はVisual Studio 2010ですが、
エラーを確認してみると
error C2440: '<function-style-cast>' : 'int' から 'Linear::Vector<NType>' に変換できません。
1> with
1> [
1> NType=double
1> ]
1> コンストラクターはソース型を持てません、またはコンストラクターのオーバーロードの解決があいまいです。
1> main.cpp(81) : コンパイルされたクラスの テンプレート のインスタンス化 'Linear::Private::RCMul<LHS,RHS>' の参照を確認してください
1> with
1> [
1> LHS=Linear::Vector<double>,
1> RHS=double
1> ]

と出ます。
RCMulのインスタンス化がおこなわれているとのことですが、この場合よりマッチするのは
operator*(const Vector<NType1>& lhs, const Vector<NType2>& rhs)だと思うのです。
実際上記の
typedef typename std::remove_const< decltype( LHS(0) * RHS(0) ) >::type type;
//typedef Empty type;
のコメントアウトを変更し
//typedef typename std::remove_const< decltype( LHS(0) * RHS(0) ) >::type type;
typedef Empty type;
とすればコンパイルは問題なく通ります。

decltypeを使用することによってコンパイルが通らなくなるということだろうという結論には至りましたが、
これはどういう理由なのでしょうか? というのが質問です。
またよい解決法をご存知であればそちらも教えていただきたく思います。
よろしくお願いします。

アバター
a5ua
記事: 199
登録日時: 14年前

Re: decltypeを使ってのプログラミングについて

#2

投稿記事 by a5ua » 14年前

コード:

double d = v * u;
の呼び出しにおいて、3つあるLinear::operator*のいずれにもマッチし、
例えば、3つ目のoperator*において、
NType1 = double
NType2 = Linear::Vector<double>
として、
RCMul<NType1, NType2>::typeをみると、
RHS(0) -> NType2(0) -> Linear::Vector<double>(0)というコンストラクタ呼び出しになり、
このような引数を取るコンストラクタが定義されていないために、エラーが出ているのだと思います。

一般的な解決方法はわかりませんが、いくつか方法を考えてみたので、参考になれば幸いです。


(その1)
内積を行う関数名をoperator*ではなく、dot_productなどにする。

(その2)
RCMulの実装を以下のようにする

コード:

template <typename LHS, typename RHS >
struct RCMul
{
	typedef typename std::remove_const< decltype( LHS() * RHS() ) >::type type;
};
(その3)
LHSとかRHSにVectorが入るのが良くないので、テンプレートの部分特殊化で回避する

コード:

template <typename LHS, typename RHS >
struct RCMul
{
	typedef typename std::remove_const< decltype( LHS(0) * RHS(0) ) >::type type;
};

template <typename LHS, typename RHS>
struct RCMul<Vector<LHS>, RHS>
{
};

template <typename LHS, typename RHS>
struct RCMul<LHS, Vector<RHS>>
{
};

アバター
a5ua
記事: 199
登録日時: 14年前

Re: decltypeを使ってのプログラミングについて

#3

投稿記事 by a5ua » 14年前

書き忘れましたが、
新しい関数宣言構文(関数の戻り値の型を後置する記法)によって、戻り値の型を簡単に書けるようになるかもしれません。

コード:

// 戻り値の型は、a + b の結果の型
template <typename A, typename B>
auto tashizan(const A &a, const B &b) -> decltype(a + b)
{
	return a + b;
}

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

Re: decltypeを使ってのプログラミングについて

#4

投稿記事 by GRAM » 14年前

>>a5uaさん

ありがとうざいます!!
関数テンプレートのオーバーロードは最良マッチのもののみインスタンス化されるかと思ったのですがそうでもないのですね。
一つ賢くなりました。
Vectorのテンプレート引数となるクラスにはデフォルトコンストラクタを要求しないので、二つ目の解決法はちょっと嫌だなぁと思っていたのですが、
そうですか・・・
3っつ目の方法があるのですね・・・
いやはや、部分特殊化で何とかしてしまうというのは思いつきませんでした。
新しい構文についてはC++0xならば書くであろうということは知っていたのですが、
VSがまだ対応していないようでしてこのようなビミョーなことをしていたのです(泣)
(どちらにしてもremove_constをしないといけないので見た目に優しいかはまだわかりませんが・・・)

本当に本当にありがとうございます。

閉鎖

“C言語何でも質問掲示板” へ戻る