Search code examples
c++googletestsfinae

Why can't I use google test's ASSERT_TRUE for my SFINAE check?


I have a simple SFINAE check for the existence of a static member function using C++17's std::void_t:

template <class T, class X = void>
struct has_bar {
    static const bool value = false;
};
template <class T>
struct has_bar<T, std::void_t<decltype(T::bar(std::declval<int&>()))>> {
    static const bool value = true;
};

The class Foo obviously has a bar member function taking an int& as its only argument, the class NoBar has no such member function:

struct Foo
{
    static void bar(int& value)
    {
        value = 42;
    }
};

struct NoBar{};

Using has_bar in a static_assert works as expected, but using it in an ASSERT_TRUE statement yields a compiler error:

TEST(SFINAE, has_bar)
{
    static_assert(!has_bar<NoBar>::value);
    static_assert(has_bar<Foo>::value);
    ASSERT_FALSE(has_bar<NoBar>::value);

    //why does this not work??
    ASSERT_TRUE(has_bar<Foo>::value);
}

int main(int argc, char** argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
ASM generation compiler returned: 0
/opt/compiler-explorer/gcc-10.2.0/bin/../lib/gcc/x86_64-linux-gnu/10.2.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/ccfSP5vL.o: in function `SFINAE_has_bar_Test::TestBody()':
/home/ce/<source>:42: undefined reference to `has_bar<Foo, void>::value'
collect2: error: ld returned 1 exit status
Execution build compiler returned: 1

Is this a google test bug or am I missing something here?


Solution

  • This error occurs because has_bar::value has been declared but not defined. Somehow, the ASSERT_TRUE macro has odr-used this object. It's not necessary to understand exactly what ASSERT_TRUE is doing; you should always define your static class members.

    The easiest way to do this is to inherit from std::false_type and std::true_type instead of declaring a static member named value. The standard library already contains a definition for the inherited value member, so you don't have to provide your own.

    In C++17, note that static constexpr bool value = false; is a definition by itself (and is implicitly inline). But this isn't the case if the member is merely const and not constexpr, so you see the issue in both C++14 and C++17 mode.