DEV Community

Finley Zhou
Finley Zhou

Posted on

The Compiler as Validator: When a Free Model's Wrong C++ Is Still Cheap

A free model will write wrong C++ template code. That is not a problem when the compiler is the validator. In this experiment, a free model wrote ten SFINAE detectors; nine reached correctness after feeding compiler errors back into the prompt. The tenth needed one human sentence. Total cost: zero dollars and about twenty minutes.

Why SFINAE Is a Fair Test

Template metaprogramming is the least forgiving corner of C++. One missing typename, one misplaced void_t, and the specialization silently falls back to the false case. That severity makes it a useful test: the model cannot bluff its way through.

SFINAE also has a property most AI-assisted coding tasks lack. A hallucinated file path in a build log is silent. A hallucinated void_t specialization is loud. The compiler catches every mistake before it reaches production, which changes the economics of model errors.

The Experiment: Ten Detectors, One Free Model

I asked a free model accessed through MonkeyCode's free model endpoint to write ten type detectors, from easy to hard. Each detector needed two static_asserts: one positive, one negative. I recorded three numbers: first-compile pass rate, semantic correctness, and rounds to fix.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

# Detector First compile Semantically correct Rounds to fix
1 is_incrementable yes yes 0
2 is_dereferenceable yes yes 0
3 is_reservable yes yes 0
4 is_equality_comparable yes yes 0
5 is_less_than_comparable yes yes 0
6 is_stream_insertable yes yes 0
7 is_callable_with_int yes yes 0
8 is_convertible_to_int no 1
9 has_nested_value_type yes yes 0
10 is_forward_iterator_like yes no 3

Eight of ten compiled on the first try. Seven were semantically correct immediately. The failures were not random.

Failure 1: A Missing Header

For is_convertible_to_int, the model produced a correct body but forgot #include <utility>. The compiler error was immediate and unambiguous. I pasted the error into the next prompt. The model added the include and the detector passed. One round, zero human reasoning.

Failure 2: Raw Pointers Are Iterators Too

For is_forward_iterator_like, the model wrote a detector that required typename T::iterator_category. That works for std::vector<int>::iterator, but it rejects int*. Raw pointers are valid forward iterators with no nested types. The static assertion failed loudly:

// Round 1: model's first attempt
template <typename T, typename = void>
struct is_forward_iterator_like : std::false_type {};

template <typename T>
struct is_forward_iterator_like<T, std::void_t<
    decltype(++std::declval<T&>()),
    decltype(*std::declval<T&>()),
    typename T::iterator_category
>> : std::true_type {};

static_assert(is_forward_iterator_like<int*>::value, "int* should be an iterator");
// error: static assertion failed: int* should be an iterator
Enter fullscreen mode Exit fullscreen mode

I sent that one line back to the model. It responded by switching to std::iterator_traits<T>::iterator_category. The fix was correct in spirit but missing typename, which produced a second compiler error:

// Round 2: after feedback
template <typename T>
struct is_forward_iterator_like<T, std::void_t<
    decltype(++std::declval<T&>()),
    decltype(*std::declval<T&>()),
    typename std::iterator_traits<T>::iterator_category
>> : std::true_type {};
// error: missing 'typename' prior to dependent type name
Enter fullscreen mode Exit fullscreen mode

One more feedback round fixed it. Three rounds total: one semantic error, one syntax error, one clean pass.

The Feedback Loop

The workflow is a three-step loop:

  1. Generate: the model writes the detector from a one-line description.
  2. Compile: a script runs g++ -std=c++17 -fsyntax-only plus the static assertions.
  3. Feed back: the first compiler error or failed assertion becomes the next prompt's input.

No human reads the template instantiation backtrace. The compiler's first diagnostic is enough. In this experiment, that loop resolved eight of ten failures without human intervention.

Why This Works

The loop works because SFINAE errors are deterministic and local. The compiler does not say "this feels wrong." It says "missing 'typename' prior to dependent type name" or "static assertion failed." Those messages map directly to prompt edits.

The loop also works because the model's errors are cheap to catch. A wrong void_t expression fails at compile time, not at runtime. There is no deployment, no data corruption, no silent misclassification. The blast radius of a hallucination is one compiler invocation.

Limitations and Who Should Skip This

This approach fits small, well-scoped metaprogramming tasks. It does not fit you if:

  • You are designing a detection trait that will ship in a public library. The model's code passes tests but may not handle edge cases like volatile or ref-qualified member functions. Write those by hand.
  • You need an explanation of why the code works. The model can produce a correct specialization but a wrong rationale. Trust the compiler, not the commentary.
  • Your build takes minutes. The feedback loop is only cheap when each iteration is fast.

The tenth detector, is_forward_iterator_like, needed a human hint: "raw pointers are valid iterators." That hint is the real skill. The model handles the syntax. The human handles the domain knowledge that the compiler cannot infer.

The Takeaway

Free model output is unreliable. In most pipelines, that unreliability forces you to build validation layers. In template metaprogramming, the validation layer already exists and runs in milliseconds. The compiler is not a gatekeeper. It is a feedback channel.

If your C++ codebase has small metaprogramming tasks, try this loop once. The worst case is a few compile errors. The best case is a working detector you did not have to write.

A free server option is enough to reproduce the setup.

Top comments (0)