c++c++20c++-concepts

Concept placement changes program outcome without compile error, possible to avoid?


I thought I wrote a nice concept, but then before the commit I reordered where it was in my file and suddenly, the program result was wrong (without any compilation errors)!

Here's a code sample to match what I had, trying to do a special compare in some cases:

#include <iostream>
#include <string>
#include <vector>

namespace
{
    //Put CanDoSpecialCompare here, and the end result is different ("Same int, Different vector"))!
    
    template<typename T>
    bool SpecialCompare(const std::vector<T>& first, const std::vector<T>& second) {
        return true; //Dummy implementation
    }

    //Put CanDoSpecialCompare here, and the end result is different ("Same int, Same vector"))!
    template <typename T>
    concept CanDoSpecialCompare = requires(T a, T b) {
        SpecialCompare(a,b);
    };

    template<CanDoSpecialCompare T>
    bool Compare(const T& a, const T& b) {
        return SpecialCompare(a, b);
    }

    template<typename T>
    bool Compare(const T& a, const T& b) {
        return a == b;
    }
}

int main() {
    std::vector<int> v1{1,2}, v2{1,3};    
    int i1 = 1, i2 = 1;
    
    if (Compare(i1,i2))
        std::cout << "Same int, ";
    else 
        std::cout << "Different int, ";

    if (Compare(v1,v2))
        std::cout << "Same vector" << std::endl;
    else 
        std::cout << "Different vector" << std::endl;
}

fiddle

I sort of see why this happens (defining the concept before the function it tests for, so it doesn't "see" the function?), but not completely (it's a template that is used only after the SpecialCompare function template it needs, so at the moment I use the concept, it should see the SpecialCompare function template?).

Also, what could I do if I wanted to have the concept at the top, or better, how can I write this so that the concept can be moved without issue? I think I could also use a trait, but my impression was always that concepts were the more readable/shorter equivalent of that?


Solution

  • As already mentioned in comments, the concept can only see names declared before itself, or introduced by ADL - and name lookup failure just counts as evaluating to false (after all, there could be a SpecialCompare introduced via ADL for some T).

    The easy solution is to add a sanity check such as

    template <typename T>
    concept CanDoSpecialCompare = requires(T a, T b) {
            SpecialCompare(a,b);
    };
    static_assert(CanDoSpecialCompare<std::vector<int>>);
    

    right after the concept.