私は DependencyInjectable 動作を実装しようとしています。この動作から派生したクラスは、インスタンス化時に適切な依存関係を注入できます。
以下では、私が抱えている問題を説明するために、はるかに大きなプロジェクトからコードのサンプルを抽出して要約しようとしました。少し長くなりましたが、ご容赦ください。
#include <tuple>
#include <type_traits>
template <typename, typename> struct tuple_has_type;
template <typename T> struct tuple_has_type<T, std::tuple<>> : std::false_type { };
template <typename T, typename U, typename ... Args> struct tuple_has_type<T, std::tuple<U, Args ...>> : tuple_has_type<T, std::tuple<Args ...>> { };
template <typename T, typename ... Args> struct tuple_has_type<T, std::tuple<T, Args ...>> : std::true_type { };
template<typename ... Dependencies>
class DependencyInjectable
{
private:
template<int index, typename ... Args>
struct assign_dependencies
{
void operator () (std::tuple<Dependencies ...> &lhs, std::tuple<Args ...> &&rhs)
{
typedef typename std::tuple_element<index, std::tuple<Dependencies ...>>::type T;
std::get<T>(lhs) = std::get<T>(rhs);
assign_dependencies<index - 1, Args ...> { } (lhs, std::forward<std::tuple<Args ...>>(rhs));
}
};
template<typename ... Args>
struct assign_dependencies<0, Args ...>
{
void operator() (std::tuple<Dependencies ...> &lhs, std::tuple<Args ...> &&rhs)
{
typedef typename std::tuple_element<0, std::tuple<Dependencies ...>>::type T;
std::get<T>(lhs) = std::get<T>(rhs);
}
};
public:
template<typename ... Args>
DependencyInjectable(Args ... dependencies)
{
setDependencies(std::forward<Args>(dependencies) ...);
}
virtual ~DependencyInjectable(void) { }
template<typename T> auto getDependency(void) const ->
typename std::enable_if<tuple_has_type<T, std::tuple<Dependencies ...>>::value, T>::type
{
return std::get<T>(m_dependencies);
}
template<typename T, typename U = typename std::decay<T>::type>
auto setDependency(T &&dependency) ->
typename std::enable_if<tuple_has_type<U, std::tuple<Dependencies ...>>::value, void>::type
{
std::get<U>(m_dependencies) = dependency;
}
template<typename ... Args>
void setDependencies(Args ... dependencies)
{
constexpr auto size = std::tuple_size<std::tuple<Dependencies ...>>::value;
static_assert(size > 0, "List of dependencies must be specified.");
assign_dependencies<size - 1, Args ...> { } (m_dependencies, std::forward_as_tuple(
std::forward<Args>(dependencies) ...));
}
private:
std::tuple<Dependencies ...> m_dependencies; // an std::tuple containing injection dependencies
};
class DependencyOfBase { };
class DependencyOfDerived { };
class Base
: public DependencyInjectable<DependencyOfBase *>
{
public:
Base(DependencyOfBase *pDependencyOfBase)
: DependencyInjectable<DependencyOfBase *>(pDependencyOfBase)
{
}
virtual ~Base(void) { }
};
class Derived
: public Base, public DependencyInjectable<DependencyOfDerived *>
{
public:
using Base::getDependency;
using DependencyInjectable<DependencyOfDerived *>::getDependency;
Derived(DependencyOfBase *pDependencyOfBase, DependencyOfDerived *pDependencyOfDerived)
: Base(pDependencyOfBase),
DependencyInjectable<DependencyOfDerived *>(pDependencyOfDerived)
{
}
virtual ~Derived(void) { }
};
int main(int argc, char **argv)
{
DependencyOfBase dependencyOfBase;
DependencyOfDerived dependencyOfDerived;
Base base(&dependencyOfBase);
Derived derived(&dependencyOfBase, &dependencyOfDerived);
auto *pDependencyOfBase = base.getDependency<DependencyOfBase *>();
auto *pDependencyOfDerived = derived.getDependency<DependencyOfDerived *>();
return (pDependencyOfBase != nullptr && pDependencyOfDerived != nullptr) ? 0 : 1;
}
特に、依存関係を DependencyInjectable クラス内に格納するために std::tuple を使用し、タプルに要求された型が含まれていない場合、enable_if を使用して getDependency() 関数を無効にしようとしています。メインでは、Derived の getDependency() 関数のインスタンスを呼び出し、「DependencyOfDerived *」を指定します。これは、指定する限り機能します
using Base::getDependency;
using DependencyInjectable<DependencyOfDerived *>::getDependency;
Derived クラスで、しかし私は enable_if が「DependencyOfDerived *」テンプレート パラメーターのベースで getDependency() 関数を無効にするだろうと考えたでしょう。ここで何が欠けていますか?
using ステートメントにコメントを付けると、gcc から次の出力が得られます。
関数 'int main(int, char**)': 116:42: エラー: メンバー 'getDependency' の要求があいまいです 45:31: 注: 候補は次のとおりです: template typename std::enable_if >::value, T> ::type DependencyInjectable::getDependency() const [with T = T; 依存関係 = {DependencyOfDerived*}] 45:31: 注意: テンプレートの型名 std::enable_if >::value, T>::type DependencyInjectable::getDependency() const [with T = T; 依存関係 = {DependencyOfBase*}] 116:76: エラー: '*' トークンの前にプライマリ式が必要です 116:77: エラー: '>' トークンの前にプライマリ式が必要です 116:79: エラー: ') の前にプライマリ式が必要です' トークン