单独的源文件中的SFINAE模板专业化

时间:2019-05-27 17:16:19

标签: c++ c++11 templates sfinae

我正在维护一个程序,该程序具有一系列基本相似的不同结构。我想编写一个利用SFINAE来允许从客户端调用此方法的模板方法。当我内联定义模板专业化时,一切都按预期工作。但是,当我尝试将模板定义移至单独的编译单元时,遇到了问题。我试图将模板实现移到一个单独的文件中,以便能够对大多数依赖类使用正向声明。以下是我正在尝试实现的示例:

// Impl.h
#pragma once

struct A {
    struct {
        int value;
    } valueA;
};

struct B {
    struct {
        int value;
    } valueB;
};

template<typename T>
int GetValue(T const &value);

// Impl.cpp
#include "Impl.h"
#include <type_traits>

using std::enable_if_t;
using std::remove_reference_t;

template<typename T, typename U, U(T::*Value)>
static inline int GetValueImpl(T const &value) {
    return (value.*Value).value;
}

template<typename T>
enable_if_t<T::valueA, int> GetValue(T const &value) {
    static constexpr auto T::*const Value = &T::valueA;
    typedef remove_reference_t<decltype(value.*Value)> ValueType;
    return GetValueImpl<T, ValueType, Value>(value);
}

template<typename T>
enable_if_t<T::valueB, int> GetValue(T const &value) {
    static constexpr auto T::*const Value = &T::valueB;
    typedef remove_reference_t<decltype(value.*Value)> ValueType;
    return GetValueImpl<T, ValueType, Value>(value);
}

template<> int GetValue(A const &); // C2912 here
template<> int GetValue(B const &); // C2912 here

我正在使用VS2017u2,并收到错误C2912:显式专门化'int GetValue(const A&)'不是功能模板的专门化。有谁知道如何在单独的编译单元中使用这些定义?

1 个答案:

答案 0 :(得分:2)

当您编写enable_if_t<T::valueA, int>时,它正在检查名为T的{​​{1}}的静态成员(可能像valueA一样是static constexpr bool valueA = /* true or false */;表示T::valueT)。

要实际检查它是否具有名为using T = std::is_same<U, V>;valueA的成员,请将其置于上下文中,如果该成员不存在,则替换错误,或者valueB 。像这样:

true

即使修复了SFINAE检查之后,模板实例化也使用了错误的语法。您应该使用:

// Pointers to member variables can never be null
// so these will always be enabled if `valueA` or
// `valueB` exist in the first place
enable_if_t<&T::valueA != nullptr, int>
enable_if_t<&T::valueB != nullptr, int>

// But that also allows pointers to static members
// so if you don't want that, you can do something else.
// Like checking if `&T::member` is a pointer to a member
// (But this is probably overkill as you have a specific set
// of types and none of those names are ever static members
// and if you didn't there is a small caveat with
// an overloaded `operator&` but that doesn't really matter)
enable_if_t<std::is_same_v<decltype(&T::valueA), decltype(T::valueA) T::*>, int>
enable_if_t<std::is_same_v<decltype(&T::valueB), decltype(T::valueB) T::*>, int>

此后,它仍然不起作用,因为template int GetValue(A const &); // No `<>` template int GetValue(B const &); template<typename T> enable_if_t<..., int> GetValue(T const&);是不同的函数,因此它应该实例化哪个是模棱两可的(因为两者都可以工作)。您需要将这两个 都设置为不同的函数(在我的示例中为template<typename T> int GetValue(T const&);),并声明GetValueImpl2的方式与标题相同:

GetValue