检测给定类型的模板实例化是否存在

Detect the existence of a template instantiation for a given type

我正在使用模板明确声明并允许读取特定数据。

#include <type_traits>

template <typename T>
struct Access
{
    template <typename U>
    void Read()
    {
        static_assert(std::is_same_v<T, U>);
    }
};

通常 T 是一组类型,但我在这里进行了简化。

我想确保声明的任何访问权限都得到实际使用。如果用户声明 Access<int> 我想检查一下是否有相应的 Read<int> 某处。

给出上下文,我目前正在尝试做的是检测 Access<int>::Read<int> 是否曾经被实例化。这可能吗?

我尝试使用 extern template 来防止隐式实例化。这里的主要问题是您必须在命名空间范围内显式写出每种可能的类型。我看不到系统地执行此操作的方法。

extern template void Access<int>::Read<int>();

int main()
{
    auto IsIntReadUsed = &Access<int>::Read<int>; // Linker error, yay!
    return 0;
}

能够在编译时、link 时或 运行 时检测到这一点是可以接受的。该解决方案不需要跨编译器可移植。适用于任何单个编译器的解决方案就足够了。任何 C++ 版本都可以接受。

这是一个用于试验的沙箱 https://godbolt.org/z/d5cco989v

// -----------------------------------------------------------
// Infrastructure

#include <type_traits>

template <typename T>
struct Access
{
    template <typename U>
    void Read()
    {
        static_assert(std::is_same_v<T, U>);
    }
};

int main()
{
    return 0;
}

// -----------------------------------------------------------
// User Code

using UserAccess = Access<int>;

void UserUpdate(UserAccess access)
{
    // Oh no, access.Read<int> is never used!

    (void) access;
}

// -----------------------------------------------------------
// Validation

template <typename TDeclared>
void ValidateAccess(Access<TDeclared>)
{
    // Write something here that can tell if Access<TDeclared>::Read<TDeclared> has been
    // used when this is called with UserAccess (i.e. ValidateAccess(UserAccess())).
    // This could also be implemented inside the Access class.
}

这是一个 link-time 解决方案。适用于 GCC、Clang 和 MSVC。

一个模板(impl::Checker<T>)声明一个友元函数并调用它。

另一个模板 (impl::Marker) 定义了该函数。如果未定义,第一个 class 将获得未定义的引用。

run on gcc.godbolt.org

#include <cstddef>
#include <type_traits>


namespace impl
{
    template <typename T>
    struct Checker
    {
        #if defined(__GNUC__) && !defined(__clang__)
        #pragma GCC diagnostic push 
        #pragma GCC diagnostic ignored "-Wnon-template-friend"
        #endif
        friend void adl_MarkerFunc(Checker<T>);
        #if defined(__GNUC__) && !defined(__clang__)
        #pragma GCC diagnostic pop
        #endif

        static std::nullptr_t Check()
        {
            adl_MarkerFunc(Checker<T>{});
            return nullptr;
        }

        inline static const std::nullptr_t check_var = Check();
        static constexpr std::integral_constant<decltype(&check_var), &check_var> use_check_var{};
    };


    template <typename T>
    struct Marker
    {
        friend void adl_MarkerFunc(Checker<T>) {}
    };
}


template <typename T, impl::Checker<T> = impl::Checker<T>{}>
struct Access
{
    template <typename U>
    void Read()
    {
        static_assert(std::is_same_v<T, U>);
        (void)impl::Marker<U>{};
    }
};


int main()
{
    Access<int> x;
    x.Read<int>();

    [[maybe_unused]] Access<float> y; // undefined reference to `impl::adl_MarkerFunc(impl::Checker<float>)'

    using T [[maybe_unused]] = Access<double>; // undefined reference to `impl::adl_MarkerFunc(impl::Checker<double>)'
}

不得不向 Access 引入一个虚拟模板参数,因为我想不出任何其他方法来检测它在 using.

中的使用