Reputation: 1243
I want to make static function in singletone template class, that would be able to deduct types of template class. The problem is, that calling static function from template class requires explicit type. The only workaround that I've came up with was template function instead if template member function.
Here is an example. The problem is that foo4 part is not working
template <class T>
class Foo
{
private:
Foo() {}
Foo(const Foo&) = delete;
Foo& operator= (const Foo&) = delete;
public:
static auto& Instance()
{
static Foo foo{};
return foo;
}
template<class K> static
auto& DeductInstance(const K&)
{
static Foo<K> foo{};
return foo;
}
};
template<class K>
auto& DeductInstance(const K&)
{
return Foo<K>::Instance();
}
void main()
{
auto& foo1 = Foo<int>::Instance(); //OK
auto& foo2 = Foo<int>::Instance(); //OK (return same example as foo1)
auto& foo3 = DeductInstance(123); //OK
auto& foo4 = Foo::DeductInstance(123); //NOT WORKING
}
Upvotes: 2
Views: 1222
Reputation: 9703
It's 2021 and we have CTAD, but I don't think it helps. I think this is very close to what you (and I!) want:
// A helper struct to provide Foo with a default template
// that isn't useful for instantiation:
struct DoNotInstantiate { DoNotInstantiate() = delete; };
template <class T = DoNotInstantiate>
class Foo
{
private:
Foo() {
static_assert(!std::is_same_v<T, DoNotInstantiate>, "You can't actually instantiate it with the default, it's just to make the static function work."); // Optional
}
// ...
public:
// ...
template<class K> static
auto& DeductInstance(const K&)
{
static Foo<K> foo{};
return foo;
}
};
So you can call Foo<>::DeduceInstance(bar);
. You still have to write the <>
but otherwise, it's perfect, I think.
If you don't want that and really want to be able to call a static on a template, naming just the template name, there's this:
template <template<typename ...> class FooType, typename T>
auto DeduceInstance(const T& x) {
return FooType<T>::DeduceInstance(x);
}
which lets you call DeduceInstance<Foo>(x)
where Foo
is just a template. But that feels round-about. I prefer Foo<>::DeduceInstance(x);
.
It would be nice if CTAD let you write
template <class T>
Foo::DeductInstance(const T&) -> T&;
or maybe it would be
template <class T>
Foo::DeductInstance(const T&) -> Foo<T>::DeduceInstance(const T&);
or something similar, basically saying that if you use the template name without a type to call a static, use the argument list to dope out which template the caller was talking about.
Upvotes: 2
Reputation: 41760
The syntax you're asking for is theorically possible using injected class names. That would make Foo::
resolve to a particular, unrelated Foo<x>::
. Here's an example:
struct BaseFoo {
template<class K> static
auto& DeductInstance(const K&);
};
template <class T>
struct Foo {
static auto& Instance() {
static Foo foo{};
return foo;
}
};
template<>
struct Foo<void> : private BaseFoo {
using BaseFoo::DeductInstance;
};
template<typename K>
auto& BaseFoo::DeductInstance(const K&)
{
return Foo<K>::Instance();
}
using MakeFooAvailable = Foo<void>;
struct Dummy : MakeFooAvailable {
auto& bar() {
// Syntax working
return Foo::DeductInstance(234);
}
};
In the class Dummy
, there's a injected class name for the base class Foo
, resolving it to Foo<void>
. Then, Foo<void>
is making BaseFoo::DeductInstance
available for name resolution in its scope.
I would advise not using this solution because it's a clever one. Clever generally mean surprising. Programmers don't expect seeing Foo
as a non template when it is. The best solution I think would be:
auto& foo1 = Foo<int>::Instance(); //OK
The simpler, the better.
Upvotes: 2
Reputation: 122228
I have to admit that I dont understand completely what you are trying to do. However, you can do either of the following two:
struct foo {
template <typename T>
static T deduce(const T& t) {
return {};
}
};
template <typename T>
T deduce_free(const T& t) {
return {};
}
int main() {
auto x = foo::deduce(1);
auto y = deduce_free(1);
}
But you cannot call a method (whehter static or not) on some instance of bar<T>
before you know what T
is.
Things have changed a bit with C++17 which has class template argument deduction, though as far as I know this works for constructors, so you would still need a I am too unexperienced with that to say something not wrong ;).foo<int>
first before you could call a foo<int>::deduce()
Upvotes: 0