特定模板(不是类型)的模板函数特化

Template function specialization for specific template ( not type )

我有一些模板化的 class 类型,例如 A、B、C,如下所示:

template < typename T >
class A{}; 

template < typename T >
class B{}; 

template < typename T >
class C{};

现在我想要一个接受任何类型的函数,比如:

template < typename T> 
void Func()
{
    std::cout << "Default " << __PRETTY_FUNCTION__ << std::endl;
}

现在我想专门化该函数以仅接受给定模板之一 class,例如:

template < typename T>
void Func<A<T>>() 
{
    std::cout << "All A Types " << __PRETTY_FUNCTION__ << std::endl;
}

这是不允许的,因为它只是部分专业化。好的。

我觉得concept可能会有帮助,但感觉我想的太复杂了。我的解决方案是:

template < typename T, template <typename > typename OUTER >
bool Check; 

template < typename INNER, template < typename > typename OUTER, template < typename> typename T>
constexpr bool Check< T<INNER>, OUTER > = std::is_same_v< OUTER<INNER>, T<INNER>>;

template < typename T >
concept IsA = Check< T, A >; 

template < typename T >
concept IsB = Check< T, B >; 


template < IsA T >
void Func()
{
    std::cout << "All A Types " <<   __PRETTY_FUNCTION__ << std::endl;
}

template < IsB T >
void Func()
{
    std::cout << "All B Types " <<   __PRETTY_FUNCTION__ << std::endl;
}

int main()
{
    Func<A<int>>();
    Func<B<int>>();
    Func<C<int>>();
}

我觉得有点复杂。那可以简化吗?如果可以删除 Check 模板就好了。有什么想法吗?

在此处查看完整示例live

一个常见的解决方法是这样的:

template <typename T> 
struct FuncImpl {
  static void Run() { std::cout << "Default"; }
};

template <typename T> 
struct FuncImpl<A<T>> {
  static void Run() { std::cout << "Specialized for A<T>"; }
};

template <typename T>
void Func() { FuncImpl<T>::Run(); }

函数模板不能部分特化,但 class 模板可以;所以我们只是从前者委托给后者。

OP 写道:

Yes, would be nice to write some "instant" code directly in the functions template parameter... if possible...

通常,为了确定它是否是某个给定 class 模板的特化,我们会提前定义一个特征,然后检查类型是否满足该特征。如果你不想提前定义任何东西,你必须想办法进行模板参数推导inline,你可以检查它是否成功。在 C++20 中,这很简单但有点难看:

template <typename T> 
void Func()
{
    std::cout << "unconstrained" << std::endl;
}

template <typename T>
requires requires {
    []<typename U>(const A<U>&){}(std::declval<T>());
}
void Func()
{
    std::cout << "constrained" << std::endl;
}

此处,requires-clause 检查是否 T 可以绑定到 const A<U>& 类型的参数以用于某些可推导的 U。你可以在这里看到这个例子:https://godbolt.org/z/dTEbaaPvh

读起来不太愉快。如果您将多次使用此 hack,我认为最好只定义特征。我回答你的问题只是为了满足你的好奇心,而不是推荐这种技术。

上面的版本不仅会在模板参数是某个 A<U> 时选择受约束的版本,而且当它可能是 cv 限定的,可能是 ref 限定的,并且可能是从 [=14= 公开派生的时].可以进行一些调整以使约束更加严格。

It feels a bit complicated to me. Can that be simplified? Would be nice if the Check template can be removed. Any idea?

很多复杂性和不雅之处在于每个 class 模板都需要一个新概念。写一个通用可重用的概念,用起来不再复杂

template <typename T, template <typename...> class TT>
constexpr bool is_instantiation_of_v = false; 

template <template <typename...> class TT, typename... TS>
constexpr bool is_instantiation_of_v <TT<TS...>, TT> = true;

template <class C, template<typename...> class TT>
concept instantiation_of = is_instantiation_of_v<C, TT>;

与您的原理相同,除了检查器可用于采用任意数量类型参数的模板。同时,该概念接受相同的参数。第一个参数具有特殊含义,在简写语法中被隐式理解为受约束的模板参数。其余部分(模板模板参数)必须明确给出。

如何使用? Like this

template <instantiation_of<A> T>
int Func()
{
    return 'A';
}

template <instantiation_of<B> T>
int Func()
{
    return 'B';
}

有新的 class 模板来约束?没问题,这个概念不需要额外的样板就可以工作。

template <instantiation_of<D> T>
int Func()
{
    return 'D';
}