Ошибка при попытке вызова метода неполного типа
Есть некоторая библиотека, которая предоставляет примерно такой интерфейс:
namespace thirparty
{
template<typename T>
class SM
{
static_assert(
requires(T sm) { { sm.operator()() }; },
"no configurable"
);
public:
constexpr SM() {}
constexpr explicit SM(T& s) {}
};
template<typename... Ts>
constexpr auto some_fun(Ts... ts)
{
return true;
}
}
Я в своих классах добавляю экземпляр thirparty::SM sm следующим образом:
template <typename T>
struct Obj1
{
int fun() const { return 42; };
struct inner
{
double a{ 3.14 };
int g() const { return 7; }
auto operator()() const
{
return thirparty::some_fun(
g()
, a
// , &Obj1::fun
);
}
};
thirparty::SM<inner> sm{}; // необходимый экземпляр
};
В такой реализации все работает. Но есть проблема: нет прямого доступа к членам класса Obj1 при формировании параметров вызова thirparty::some_fun().
При попытке решить эту задачу, я пришел к такому решению:
template<typename T>
struct SMAdapter
{
T* obj;
auto operator()() const
{
return std::apply(
[](auto&&... args)
{
return thirparty::some_fun(args...);
},
obj->args() // error C2027: use of undefined type 'Obj<int>'
);
}
};
template<typename T>
struct Obj
{
using Self = Obj<T>;
int fun() const { return 42; }
double a{ 3.14 };
auto args()
{
return std::tuple
{
fun()
, a
};
}
SMAdapter<Self> sm_adapter{ this };
thirparty::SM<decltype(sm_adapter)> sm{ sm_adapter }; // необходимый экземпляр
};
В данном случае аргументы для thirparty::some_fun() формируются с прямым доступом к членам Obj. Но этот код не компилируется с ошибкой:
error C2027: use of undefined type 'Obj'
при создании экземпляра Obj<int> obj{};
Как я понял, проблема в том, что идет обращение к члену объявленного класса Obj до его реализации. Есть ли способ обойти эту проблему?
Или, возможно, существует другое решение?
UPD: полный пример
#pragma once
#include <tuple>
/*===========================================================================*/
namespace thirparty
{
template<typename T>
class SM
{
static_assert(
requires(T sm) { { sm.operator()() }; },
"no configurable"
);
public:
constexpr SM() {}
constexpr explicit SM(T& s) {}
};
template<typename... Ts>
constexpr auto some_fun(Ts... ts)
{
return true;
}
}
/*===========================================================================*/
template <typename T>
struct Obj1
{
int fun() const { return 42; };
struct inner
{
double a{ 3.14 };
int g() const { return 7; }
auto operator()() const
{
return thirparty::some_fun(
g()
, a
// , &Obj1::fun
);
}
};
thirparty::SM<inner> sm{};
};
/*===========================================================================*/
template<typename T>
struct SMAdapter
{
T* obj;
auto operator()() const
{
return std::apply(
[](auto&&... args)
{
return thirparty::some_fun(args...);
},
obj->args()
);
}
};
template<typename T>
struct Obj
{
using Self = Obj<T>;
int fun() const { return 42; }
double a{ 3.14 };
auto args()
{
return std::tuple
{
fun()
, a
};
}
SMAdapter<Self> sm_adapter{ this };
thirparty::SM<decltype(sm_adapter)> sm{ sm_adapter };
};
void test_2()
{
Obj1<int> obj1{};
Obj<int> obj{};
}
UPD2:
Возможно, я запутанно описал проблему, описать проблему простыми словами - не лучшая моя сторона.
Создал пример в godbolt. Ошибка возникает, если снять комментарий со строки Obj<int> obj{};
в функции main()
.
Есть ли возможность доработать этот пример, чтобы он компилировался?
Ответы (1 шт):
Вот минимальный пример:
#include <utility>
template<typename x_WithMethod>
struct Foo
{
// error C2027: use of undefined type 'Bar'
decltype(::std::declval<x_WithMethod>().method()) field;
};
struct Bar
{
int method() { return 0; }
Foo<Bar> bar;
};
Это происходит потому, что при инстанцировании Foo<Bar>
тип Bar
еще является неполным (incomplete), и нельзя пытаться вызывать его методы в объявлениях Foo
. В коде из вопроса в объявлении thirparty::SM<decltype(sm_adapter)> sm{ sm_adapter };
инстанцируется кдасс SM
и его static_assert
дергает sm.operator()
, который дергает SMAdapter::operator()
, возвращаемые тип которого зависит от obj->args()
, который дергает Obj<int>::args()
, в то время как тип Obj<int>
является неполным.
¿Что же делать? Можно добавить требование полноты типа, который передается шаблонным параметром в SM
или SMAdapter
или можно разорвать цепочку зависимостей на неполный тип, например явно задав возвращаемый тип SMAdapter::operator()
как bool
или добавив трейт для Obj
, который уже будет полным при инстанцировании SM
или просто доп типы вместо вывода возвращаемого значения метода.
template<typename x_WithType>
struct Foo
{
typename x_WithType::Type field;
};
struct Bar
{
using Type = int;
Foo<Bar> bar;
};