let c++ have static_assert without interruption

how can we make c++ have static_assert without interruption or disruption, simply instead skipping it

let's realize this illustration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <cstdint>
#include <iostream>
using namespace std;

template <class T=int>
class MyClass{
	static_assert(is_same_v<T, std::uint16_t>, ""); //Illustration only for 
         the must-be solved one
        T vs;
//...	 
};


int main(){
	MyClass a; // T defaut type is int
	MyClass<uint16_t> b; // not violating
	MyClass<long> c; // must not compile nor exit, instead must put 'Go on' out
	cout<<"Go on \n";

// ...
}
Last edited on
In other words, you would like
1
2
3
4
5
int main(){
    MyClass<long> foo; // would fail assertion
    cout << "Go on \n";
    foo = 42;
}

to compile like you had written:
1
2
3
4
int main(){
    cout << "Go on \n";
    foo = 42; // error: unknown identifier 'foo'
}


The static_assert stops compilation early, before errors accumulate. You desire something entirely different.
Perhaps this is what you want?
1
2
3
4
5
6
7
template <typename T> void fn()
{ 
  if constexpr (std::is_same_v<T, std::uint16_t>)
    std::cout << "fn<std::uint16_t>\n"; 
  else 
    std::cout << "fn<something_else>\n";
}
Last edited on
yeah i think if constexpr is the closest to solution
Last edited on
> MyClass<long> c; // must not compile nor exit

We could use the null-object pattern https://en.wikipedia.org/wiki/Null_object_pattern
so that our program behaves 'as-if' that part of the code was not compiled.

This caters to situations where the non-null object has member variables,
and also avoids the proliferation of boiler-plate constexpr if constructs if there are many member functions.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <type_traits>

// the base template is an empty class; does nothing at all (null behaviour, null state)
template < typename T = int, typename = void > struct S // null object
{
    constexpr S() {} // do nothing
    template < typename U > constexpr S(U) {} // do nothing

    constexpr void foo() const {} // do nothing
    void bar() {} // do nothing

    // ,,,

    constexpr friend void operator<< ( std::ostream&, S ) {} // do nothing
};

// specialisation for integral types (non-empty; has non-trivial behaviour and state)
template < typename T > struct S < T, typename std::enable_if< std::is_integral<T>::value >::type >
{
    explicit constexpr S( T tt = {} ) noexcept : t(tt) {}

    // do something
    void foo() const { std::cout << "t == " << t << '\n' ; }
    void bar() { ++t ; }

    // ,,,

    T t ; // holds state

    friend std::ostream& operator<< ( std::ostream& stm, S<T> s )
    { return stm << "S<integral type>{" << s.t << '}' ; }
};

int main()
{
    S<int> si ;
    si.bar() ;
    si.foo() ;
    std::cout << si ;

    // this would certainly be compiled (for example, we can't have syntax errors)
    // but the code would (should) be optimised away
    S<void> sv ; // null object; does nothing
    sv.bar() ;
    sv.foo() ;
    std::cout << sv ;

    std::cout << "\n\nthis is at the end of main\n" ;
}

I imagine (a variation on) the pattern @JLBorges demonstrates will be used in library implementations making use of std::is_constant_evaluated in C++20 and later.
Registered users can post here. Sign in or register to post.