Vorac
Vorac

Reputation: 9124

How to prevent accidental emission of constexpr functions

Ben Deane mentions the throw trick, which ensures a link error when a constexpr function is used in a non-constexpr context.

Here is my take:

#include <iostream>

struct Exc;

constexpr int foo( int a )
{
    if( a == 42 )
    {
        throw Exc{};
    }

    return 666;
}


int main()
{
    constexpr auto ret = foo(43);
    std::cout << ret << "\n";

    return ret;
}

But I couldn't make it work (clang++ 3.8.1-23 and g++ 6.3.0):

$ clang++ -std=c++14  main.cpp 
main.cpp:9:15: error: invalid use of incomplete type 'ExcBase'
        throw ExcBase{};
              ^~~~~~~~~
main.cpp:3:8: note: forward declaration of 'ExcBase'
struct ExcBase;
       ^
1 error generated.

A comment in this thread suggests another trick:

#include <iostream>

constexpr int foo( int a )
{
    if( a == 42 )
    {
        (void)reinterpret_cast<int>(a);
    }

    return 666;
}

int main()
{
    constexpr auto ret = foo(43);
    std::cout << ret << "\n";

    return ret;
}

Which works: no errors or warnings; when the invocation is replaced with foo(42), clang returns:

$ clang++ -std=c++14 main.cpp 
main.cpp:19:20: error: constexpr variable 'ret' must be initialized by a constant expression
    constexpr auto ret = foo(42);
                   ^     ~~~~~~~
main.cpp:10:15: note: reinterpret_cast is not allowed in a constant expression
        (void)reinterpret_cast<int>(a);
              ^
main.cpp:19:26: note: in call to 'foo(42)'
    constexpr auto ret = foo(42);
                         ^
1 error generated.

Which is great. But gcc compiles the code happily in both cases. And now the question. How can a constexpr function be written in such a way, that it cannot be used in non-constexpr environments?

Upvotes: 2

Views: 293

Answers (1)

marcinj
marcinj

Reputation: 50026

The problem is you actually instantiate (call constructor) a struct which is of incomplete type. This trick you talk about requires any symbol which will not be found at link time. So instead of struct you may use int:

http://coliru.stacked-crooked.com/a/3df5207827c8888c

#include <iostream>

extern int Exc;

constexpr int foo( int a )
{
    if( a == 42 )
    {
       throw Exc;
    }

    return 666;
}


int main()
{
    // Compiles
    constexpr auto ret = foo(43);
    std::cout << ret << "\n";

    // This will show linker error as expected:
    // /tmp/ccQfT6hd.o: In function `main':
    // main.cpp:(.text.startup+0x4c): undefined reference to `Exc'
    // collect2: error: ld returned 1 exit status
    int nn;
    std::cin >> nn;
    auto ret2 = foo(nn);

    return ret;
}

Upvotes: 5

Related Questions